CSSだけで使いやすく汎用性の高いタブ切り替え(複数設置対応)を実装する

CSSだけで使える汎用性の高いタブ切り替え

先日コメントにてCocoonで使える[ショートコード]のオプションと使い方まとめで実装しているタブ切り替えについて質問をいただきました。

これ↓

タブ切り替えのスクリーンショット

このタブ切り替えは、ラジオボタンを利用してCSSだけで切り替えを実装出来るこちらのページを参考にカスタマイズしたものです。

参考 【CSS】CSSだけでタブ切り替えを作る方法 – bagelee(ベーグリー)

コメント欄でもこんなコードで実装出来るよ~ってやり取りがあって色々考えたんですけど、
ふともっと汎用性の高いコーディング出来るんじゃ?と思い立ったので回答したコードの改良版を紹介します。

スポンサーリンク

★汎用性の高いタブ切り替えHTML&CSS

このコードの良い点
  • タブが増えてもCSSの追加が必要ない
  • CSSを追加せずに同じページで複数のタブ切り替えが使える
  • アコーディオンメニュー的なレイアウトにも出来る

これだけのコードで出来るよって事で最低限のスタイルのみ適用してます。

See the Pen タブ切り替え(汎用版) by 草村 (@kusamura_mono) on CodePen.38847

HTML

<div class="tab-wrap">
    <input id="TAB-01" type="radio" name="TAB" class="tab-switch" checked="checked" /><label class="tab-label" for="TAB-01">ボタン 1</label>
    <div class="tab-content">
        コンテンツ 1
    </div>
    <input id="TAB-02" type="radio" name="TAB" class="tab-switch" /><label class="tab-label" for="TAB-02">ボタン 2</label>
    <div class="tab-content">
        コンテンツ 2
    </div>
    <input id="TAB-03" type="radio" name="TAB" class="tab-switch" /><label class="tab-label" for="TAB-03">ボタン 3</label>
    <div class="tab-content">
        コンテンツ 3
    </div>
</div>

CSS

.tab-wrap {
    display: flex;
    flex-wrap: wrap;
}
.tab-label {
    color: White;
    background: LightGray;
    margin-right: 5px;
    padding: 3px 12px;
    order: -1;
}
.tab-content {
    width: 100%;
    display: none;
}
/* アクティブなタブ */
.tab-switch:checked+.tab-label {
    background: DeepSkyBlue;
}
.tab-switch:checked+.tab-label+.tab-content {
     display: block;
}
/* ラジオボタン非表示 */
.tab-switch {
    display: none;
}

デザインパターン

HTMLは基本のまま、CSSだけで多様なデザインが可能です。参考にどうぞ!

ボタンが幅に合わせて伸縮する

タブが増減してもタブ幅が勝手に調整されるよ。

See the Pen タブ切り替え(汎用版/装飾) by 草村 (@kusamura_mono) on CodePen.38847

アコーディオンメニュー風

2019-06-27: HTMLがボタン+コンテンツになってるのでアコーディオンメニューみたいにも出来る

See the Pen タブ切り替え(汎用版/アコーディオン) by 草村 (@kusamura_mono) on CodePen.38847

左右2カラムにする(ボタン縦)

2019-06-27: flex-direction: column;で2カラムに出来ます。height/widthの指定が必要。

See the Pen タブ切り替え(汎用版/コンテンツ横並び) by 草村 (@kusamura_mono) on CodePen.38847

スライドするタブ切り替え

See the Pen CSSだけでスライドするタブ切り替え by 草村 (@kusamura_mono) on CodePen.38847

解説はこちら

使い方・コード解説

超ざっくり解説

HTML解説

<input id="TAB-01" type="radio" name="TAB" checked="checked" /><label for="TAB-01">ボタン 1</label>
<input id="TAB-02" type="radio" name="TAB" /><label for="TAB-02">ボタン 2</label>
<input id="TAB-03" type="radio" name="TAB" /><label for="TAB-03">ボタン 3</label>

※ボタンになる部分を見やすいように余計な記述を削除して抜粋しています。

  1. <input>name属性に同じ名前を付ける。
    name属性を同じ名前にする
  2. 1行の<input>id属性<label>for属性に同じ名前を付ける。
    id属性とfor属性を同じにする

名前は他と被らなければなんでも大丈夫です。

<input>checked=”checked”が指定されたタブが初期状態でアクティブになります。

同じページに複数タブ切り替えがある場合気をつける事

<!-- 1つめのタブ切り替え -->
<input id="TAB01-01" type="radio" name="TAB01" /><label for="TAB01-01">ボタン 1</label>
<input id="TAB01-02" type="radio" name="TAB01" /><label for="TAB01-02">ボタン 2</label>
<input id="TAB01-03" type="radio" name="TAB01" /><label for="TAB01-03">ボタン 3</label>

<!-- 2つめのタブ切り替え -->
<input id="TAB02-01" type="radio" name="TAB02" /><label for="TAB02-01">ボタン 1</label>
<input id="TAB02-02" type="radio" name="TAB02" /><label for="TAB02-02">ボタン 2</label>
<input id="TAB02-03" type="radio" name="TAB02" /><label for="TAB02-03">ボタン 3</label>

※ボタンになる部分を見やすいように余計な記述を削除して抜粋しています。

  1. タブ切り替え毎に<input>name属性にそれぞれ別の名前を付ける。
    name属性がかぶらないようにする
  2. <input>id属性<label>for属性別のタブ切り替えとかぶらないように気をつける。※もちろん同タブ切り替え内でもかぶらないように

name、id、for属性がサンプルではTAB~になっていますが好きな名前で大丈夫です。
例えばBANANA、apple等でも問題ありません。

CSS解説

デフォルトでコンテンツを非表示にしています。

.tab-content {
    display: none;
}

:checkedを使いラジオボタンが選択された時に表示します。

.tab-switch:checked+.tab-label+.tab-content {
    display: block;
}

ラベルは上に並ぶようにしています。

.tab-wrap { 
    display: flex;
    flex-wrap: wrap;
}
.tab-label {
    order: -1;
}

もっとHTMLをシンプルに

ボタン周辺のコード長くて見づらい…って感じならclass属性撤廃しても大体大丈夫です。

<input id="TAB-01" type="radio" name="TAB" /><label for="TAB-01">ボタン 1</label>

CSSは次のように変更

  1. .tab-switch.tab-wrap>input[type="radio"]に変更
  2. .tab-label.tab-wrap>labelに変更

checkedがなくても初期状態でタブを表示する

何らかの事情でcheckedを指定出来ない場面で使える手法です。

See the Pen タブ切り替え(汎用版/checked無し対応) by 草村 (@kusamura_mono) on CodePen.38847

HTML

HTMLはこちらのclassにtab-orderを追加します。

最後のタブを左に移動したくない場合は追加しなくてOKです。

<div class="tab-wrap tab-order">

CSS

.tab-wrap {
    display: flex;
    flex-wrap: wrap;
}
.tab-label,
.tab-switch:checked~.tab-switch:last-of-type+.tab-label {
    color: White;
    background: LightGray;
    margin-right: 5px;
    padding: 3px 12px;
}
.tab-label{
    order: -1;
}
.tab-content,
.tab-switch:checked~.tab-content:last-of-type { 
    width: 100%;
    display: none;
}
/* アクティブなタブ */
.tab-switch:checked+.tab-label,
.tab-label:last-of-type {
    background: DeepSkyBlue;
}
.tab-switch:checked+.tab-label+.tab-content,
.tab-content:last-of-type {
    display: block;
}
/* 最後のタブを左に */ 
.tab-order .tab-label:last-of-type {
    order: -2;
}
/* ラジオボタン非表示 */
.tab-switch {
    display: none;
}

どうなってる?

デフォルトで最後のタブをアクティブ状態に。

/* 最後のラベル */
.tab-label:last-of-type { /* アクティブ */ }

/* 最後のコンテンツ */
.tab-content:last-of-type { /* アクティブ */ }

最後のタブ以外がアクティブなら、最後のタブを非アクティブに。

/* 選択されたラジオボタンがある場合 ~ 最後のラジオボタン + のラベル */
.tab-switch:checked ~ .tab-switch:last-of-type + .tab-label { /* 非アクティブ */ }

/* 選択されたラジオボタンがある場合 ~ 最後のコンテンツ */
.tab-switch:checked ~ .tab-content:last-of-type { /* 非アクティブ */ }

デフォルトで最初のタブがアクティブなように見せるために「最後のタブを左」へ移動。これもorderで解決…。

.tab-order .tab-label:last-of-type {
    order: -2;
}

WordPressで使う際の注意点

クラシックエディタでこのHTMLコードを使おうとすると勝手に<p>で囲まれるので使えません。

TinyMCE Advancedプラグインで段落を保持するように設定すると使えるかもしれません。

ブロックエディタではカスタムHTMLに記述すれば使えました。

Cocoonサイドバーウィジェットで利用する

2019-09-19追記:

初期状態でタブがアクティブにならない場合があります。
その場合#checkedがなくても初期状態でタブを表示するのCSSを利用する必要があります。

このCSSでは「最後のタブ」が「初期アクティブタブ」になります。
HTMLのchecked=”checked”を最後のタブに指定するか、指定自体なくすと良いです。

Cocoonサイドバーで意図した動作にならない理由について
もっと詳しい解説を知りたい方は#コメント欄にてご確認ください。

【小話】よくあるタブ切り替え

See the Pen よくあるタブ切り替え by 草村 (@kusamura_mono) on CodePen.38847

<div class="tab-wrap">
    <input id="TAB-01" type="radio" name="TAB" class="tab-switch" /><label class="tab-label" for="TAB-01" checked="checked">ボタン 1</label>
    <input id="TAB-02" type="radio" name="TAB" class="tab-switch" /><label class="tab-label" for="TAB-02">ボタン 2</label>
    <input id="TAB-03" type="radio" name="TAB" class="tab-switch" /><label class="tab-label" for="TAB-03">ボタン 3</label> 
    <div class="tab-content" id="TAB-01-content">
        コンテンツ 1
    </div>
    <div class="tab-content" id="TAB-02-content">
        コンテンツ 2
    </div>
    <div class="tab-content" id="TAB-03-content">
        コンテンツ 3
    </div>
</div>

ざっくり言うとこういう並びのHTMLです。

ラジオボタン1 + ラベル1
ラジオボタン2 + ラベル2 
ラジオボタン3 + ラベル3 
コンテンツ1
コンテンツ2
コンテンツ3

私も参考にさせてもらったタブ切り替えで、よく見かけるHTMLでの実装方法かなと思います。

これの面倒くさい所は、コンテンツを内包する要素にidを設定する&設定したidをCSSに記述する必要がある所ですね。
なぜかというとそれぞれのラジオボタンとコンテンツの紐付けができないから。

CSSがこんな感じで間接セレクタ(~)とidで記述しています。

#TAB-01:checked ~ #TAB-01-content,
#TAB-02:checked ~ #TAB-02-content,
#TAB-03:checked ~ #TAB-03-content {
    display: block;
}

これでは一つのページに複数設置がしづらいので、私は:nth-of-type(n)で対応する方法をとっていました。

.tab-switch:first-of-type:checked ~ .tab-content:first-of-type,
.tab-switch:last-of-type:checked ~ .tab-content:last-of-type,
.tab-switch:nth-of-type(2):checked ~ .tab-content:nth-of-type(2) { 
    display: block; 
}

これなら複数のタブ切り替えに対応可能です。

ただ、コードも長いしタブが増えたら:nth-of-type(n)を追加するって形がどうも気持ち悪いな~と心の片隅で引っかかっていた所、タブ切り替えについてコメントを頂いて再考する機会がありました。

残念ながら回答の段階では思いつかなかったんですが汗

理想のHTML

ラジオボタンとコンテンツがなぜ紐付けし辛いかっていうと隣接してないからですよね。

こんな風になってるHTMLだと

ラジオボタン1 + ラベル1
ラジオボタン2 + ラベル2 
ラジオボタン3 + ラベル3 
コンテンツ1
コンテンツ2
コンテンツ3

前述のように間接セレクタ(~)を使う必要がある。
でも間接セレクタだと後方にある要素(コンテンツ1~3)全部に当てはまるので、それぞれにidを付ける必要がある。

だから隣接するように、こう出来ればいい。

ラジオボタン1 + ラベル1 + コンテンツ1
ラジオボタン2 + ラベル2 + コンテンツ2
ラジオボタン3 + ラベル3 + コンテンツ3

こう出来なかった理由は、上部にメニューを並べて下にコンテンツが表示されるのが理想なのでレイアウト的に都合が悪かったから。

ここで唐突にひらめく。

orderで入れ替えちゃえば解決するじゃん!?

もとよりラジオボタンは非表示なんで放って置いて、ラベルにorder:-1;を設定するだけでいとも簡単にラベルだけ並べる事が出来ました。Amazing…。

って事で、理想のHTML・CSSにラジオボタン:checked+ラベル+コンテンツだけで切り替えが出来るようになりました。

あとがき

まるで世紀の大発見のような気持ちだったけど、実はもう一般的だったらちょっと恥ずかしい…。

flexが使えるようになってレイアウトの幅が広がったなぁと感じます。

HTML的にも見出し+コンテンツのような順で記述出来てコードもシンプルになったのでちょっと気分がいいです。

汎用性は高くなったと思うんですが、実際使うとレイアウトの自由度が制限される事が出てきたりするんでしょうかね。

Edge未対応ですが<details>なんてタグも存在してたりするのでいつかCSSすら不要な時代もくるかもしれませんね。

結論:orderマジ便利。

スライドするタブ切り替え

コメント

  1. Thanks! より:

     タブ切り換えの機能を使いたかったので参考にさせていただきました。ありがとうございます! ただ、私のサイトに導入させていただいたのですが、上手くいかないところがあるため質問させていただきます。

     草村さんの記事上のタブは「ボタン1」がデフォルトでアクティブになっています。しかし、私のサイトでは「ボタン1」がアクティブになりません。ページを読み込んだ直後はコンテンツが隠れた状態になっています。
     私のサイトでも「ボタン1」をデフォルトでアクティブにしたいです。解決方法があれば、教えていただけると助かります。

    —————-
    ■利用環境など
    ・利用ブラウザ:Google Chrome
    ・cocoonバージョン:1.9.7
    ・さくらインターネット スタンダード
    ・サイトURL:h ttps://majikanote.com/

    ■試したこと
    ・Chrome ―― cocoon「Lazy Load機能」停止 → Chromeキャッシュクリア → 「ボタン1」アクティブにならず
    ・Firefox ―― 表示確認 → 「ボタン1」アクティブ状態ではない

    ■設定状況
    ・タブ設定箇所 → サイドバー。ウィジェット名「ランキング」
    ・「ボタンが幅に合わせて伸縮」する、ボタン4つのタイプのソースをそのまま使用(ソースは ほぼ変更なし)
    ・ボタンは4つ「Day(ボタン1)」「Week(ボタン2)」「Month(ボタン3)」「Year(ボタン4)」
    ・ランキングはショートコードを利用して表示

    ■やりたいこと
    ・デフォルトで「Day」ボタンが選択された状態にし、tab-content のランキングも表示された状態にしたい
    —————-

    お手数おかけいたしますが、よろしくお願いします。

    • Thanks! より:

      「checkedがなくても初期状態でタブを表示する」の内容を応用し、「.tab-label:last-of-type {order: -3;}」 としたところ解決できました。

      • 草村草村 より:

        こんにちは!ご質問の件、解決出来たようでよかったです!

        解決方法も書き込んでいただけたので、同じように困っている方の助けになると思います!ありがとうございます!

        ▼一応解説すると、
        Cocoonテーマをお使いの場合、サイドバーウィジェットはスマホのサイドバー用に同じウィジェットが複製されるため同じname属性のタブ切り替えが2箇所存在してしまうんですよね^^;

        同じname属性グループ内に複数checkedの指定があると後に記述された方が優先されるため、スマホでは意図したボタンがアクティブになるんですがPCではアクティブなボタンが存在しない状態になります(スマホサイドバー側のボタンがアクティブになるので)。

        同じname属性グループ
        PCサイドバーのタブ切り替え

        [◎ボタン1] [ボタン2] [ボタン3]

        スマホサイドバーのタブ切り替え(複製)

        [●ボタン1] [ボタン2] [ボタン3]

        ◎●=「checked=”checked”」を指定したタブ

        • name属性が同じなのでPCとスマホ2箇所のタブ切り替えがグループ化されてしまう
        • [◎ボタン1]よりも後に記述された[●ボタン1]のcheckedが優先されアクティブになる
        • PCサイドバーのタブ切り替えにアクティブ(checked)が存在しない=表示されない

        なので「checkedがなくても初期状態でタブを表示する」で.tab-wrapで囲まれた最後のタブをアクティブにする事で解決するという感じです。
        今回の場合最後のボタンにcheckedを指定するか、あえてどこにも指定しない事で初期アクティブタブをPC/スマホで一致させる事が出来るかと思います。

        解説が複雑で上手く伝えられないのですが、同じようにお困りの方は「#checkedがなくても初期状態でタブを表示する」をお試しください。

        Thanks!さん有用なコメントありがとうございました!

        *2019-09-19記事内容を少し変更しました。

      • Thanks! より:

        コメント欄を読んだ方が誤解してはいけないので訂正します。
        私は、「.tab-label:last-of-type {order: -3;}」で解決できたと思っていましたが勘違いでした。
        草村さんのコードの「.tab-label:last-of-type {order: -2;}」で、現在は正常に動作しています。
        訂正して、お詫びいたします。

        • 草村草村 より:

          コードを変更せずとも動作したという事ですね。
          自分も少し自信がない部分だったので正常動作してるとの事、ご報告いただけて安心いたしました(*^^*)
          わざわざコメントくださりありがとうございました!

          • 初めまして^^

            ワードプレスで趣味ブログを書いていますが、いつもカスタマイズの参考にさせていただいております。

            今回トップページにタブ切り替えを導入しました。こちらの記事がとても分かりやすく満足したカスタマイズになり、ものぐささんのサイトを紹介するリンクを貼った記事を公開させていただきました。

            一応ご連絡をと思いましてコメントさせていただきます。

            • 草村草村 より:

              はっちゃんさん

              この記事が役立って、ご満足いただけたようで光栄です。
              リンクまで貼ってくださる方ってそう多くないので嬉しいです~ブログ素敵なカスタマイズですね!(*^^*)
              お返事が遅くなってしまい申し訳ない思いですが、この度はコメントありがとうございました!またお役に立てましたら幸いです。

  2. Itsuki より:

    タブ切り替え機能の実装方法を探していたので助かりました。ありがとうございます。
    ところで、こちらのタブ切り替えは入れ子にすること(1つのタブののなかにもう一つのタブ切り替えを作る)は可能でしょうか。自分で少し試してみたのですが、うまくいかず、お力を貸していただけると幸いです。

  3. のぼり より:

    草村さま

    初めまして。のぼりと申します。
    趣味で個人サイトを運営しております。
    タブを導入したいと考えていた折、こちらの記事にたどり着きました。
    わかりやすく、また導入しやすく、たいへん助かりました! ありがとうございます。

    実際に導入してみてわからなかったところがありましたので、質問させていただきます。
    リンクで戻る際に、任意のタブを開くよう指定することはできますでしょうか。

    例えば、ページAに
     TAB-01(checked)
     TAB-02
    の二つのタブを作ります。
    TAB-02からはページBへのリンクを張ります。
    ページBからページAに戻るリンクを張る際、通常TAB-01がcheckedなのでTAB-01がアクティブ状態で表示されますが、ページBへのリンクはTAB-02にあったため、TAB-02がアクティブ状態で表示されるようリンクで指定する。
    という動作です。

    初歩的な質問でしたら申し訳ありません。
    お時間のある時にご教授いただけたら幸いです。よろしくお願いいたします。

    • 草村草村 より:

      のぼりさん

      はじめまして!コメントありがとうございます。
      当記事がお役に立てたようで嬉しいです(*^^*)

      ご質問の件ですが、
      アクティブなタブを制御したい場合、javascriptを利用する事になるかと思います。
      下記のサンプルコードは「URLパラメータで指定されたidの要素にcheckedを付与する」という動作です。

      window.onload = () => {
      	let params = new URLSearchParams(document.location.search.substring(1));
      	let tab = params.get("tab");
      	let checkbox = document.getElementById(tab);
      	if (checkbox) {
      		checkbox.checked = true;
      	}
      };

      コードペンにサンプルを用意したのでご確認ください。

      ▼コードペン
      https://codepen.io/kusamura_mono/pen/MWpqeYY
      ※URLパラメータ無しなので初期アクティブタブは「ボタン1」

      ▽同じアドレスにURLパラメータ(?tab=TAB-02の部分)を指定
      https://codepen.io/kusamura_mono/pen/MWpqeYY?tab=TAB-02
      ※ボタン2のidを指定しているためアクティブタブが「ボタン2」になります。
      ※TAB-02をTAB-03にするとアクティブタブが「ボタン3」になります。

      ▼軽く解説
      ・ページA(タブ切り替えが設置されているページ)=http://www.example.com/pageA.html
      と仮定してページBに張るリンク先を「http://www.example.com/pageA.html?tab=TAB-02」のようにパラメータ(?tab=TAB-02)を付与したリンクにしてください。
      ・URLの「TAB-02」の部分はアクティブにしたいタブのidを指定してください。
      ・javascriptはページA(タブ切り替えが設置されているページ)に設置する必要があります。

      以上、参考になれば幸いです。

      • のぼり より:

        草村さま

        早速お返事いただきありがとうございます。
        お教えいただいたjavascriptを導入しましたら、タブの制御に無事成功いたしました。
        おかげさまでサイト全体がすっきりとした形になり、本当に感謝しております。
        お忙しいなか、ありがとうございました!

        のぼり

        • 草村草村 より:

          のぼりさん

          上手くいったようでよかったです。
          同じようにカスタマイズしたい方のヒントにもなったと思います。
          コメントありがとうございました!

  4. すい より:

    初めまして
    こちらの記事大変参考にさせていただいておりますが、タブを5つ以上使うと表示がうまくいかないのですが、解決策ありますでしょうか?
    よろしくお願いいたします。

    • 草村草村 より:

      すいさん

      「表示がうまくいかない」が具体的にどのような状態かわからないため、回答が難しいです。

      おそらくHTMLのミスではないかと思いますので#HTML解説を一度ご確認ください。
      コピペでタブを増やした場合”idとfor属性の名前”を”他とかぶらないように変更する”必要があります。
      解説で言うと「1行の<input>のid属性と<label>のfor属性に同じ名前を付ける」
      「名前は他と被らなければなんでも大丈夫」の部分です。

      コードペンにタブ8つバージョンのコードを用意しましたのでご確認ください。
      コピペして使っていただいても大丈夫です。
      https://codepen.io/kusamura_mono/pen/KKXQwzq
      ※CSSは#ボタンが幅に合わせて伸縮するのコードを使用。

      以上、参考になれば幸いです。

  5. ロキ より:

    初心者質問で大変申し訳ございません。。
    こちらのコードを利用させていただいたのですが、うまくタブが表示されません。。
    テーマはJINを使用しているのですが、テーマによってうまくいかないということはありますでしょうか?

    • 草村草村 より:

      ロキさん

      どのような状態かわからないので確かな事はわかりませんが、テーマによってはCSSが干渉してしまって表示がおかしくなる事はあると思います。
      class名を他と絶対被らないであろう名称に変更しても表示されないようでしたら、テーマが初期値として設定しているCSSが干渉しているか、HTMLが間違っている可能性があります。
      それ以上の事は実際に見てみないとちょっと私にはわからないです。(見ても分からない場合もあります)

  6. 匿名 より:

    失礼します。

    切り替えタブを利用して、カテゴリーの記事を表示させたい場合、HTMLにある「コンテンツ 1」に何を入力したらカテゴリーの記事が表示されるのでしょうか?

    検索して調べてみるものの、「ここに要素を入れる」「コンテンツ①をここに入れる」などと記載されており、また、Cocconのショートコードを利用していたり(私はCocconではありません)と、素人には「…」の状態です。

    イメージはSWELLのトップページようなカテゴリーごとに切り替える感じです。
    https://swell-theme.com/function/318/

    ご負担で無ければ教えて頂ければ幸いです。

    • 草村草村 より:

      匿名さん

      何を入力すればいいかはテーマによって異なるかと思います。
      Cocoonの場合はショートコードが用意されていますので簡単ですが、お使いのテーマや使用する箇所によってはPHPの知識が必要かもしれません。
      あなたがお使いのテーマの「カテゴリーの記事を表示する方法」をご自身でお調べになって、そのコードを「コンテンツ1」に入力してください。

タイトルとURLをコピーしました