WWW Watch

CSS だけで作るタブ切替ユーザインタフェース (float 使用版)

:target 疑似クラスを使って、JavaScript は一切使用せず CSS だけでタブ切替のユーザインタフェースを実装するテストの、position: absolute; を使わない版です。

CSS1つ前のエントリーで、:target 疑似クラスを利用した CSS だけで作るタブ切替 UI を実装してみるテストしてみました。詳しいことは下記のエントリーをご覧ください。

先のエントリーで作ったサンプルは、position: absolute; を使って、各タブの内容を重ねちゃった上で表示を切り替えるっていう方法を使ったのですが今回は、float プロパティを使ったサンプルを紹介してみます。

CSS だけで作るタブ切替ユーザインターフェース

実際のサンプルは下記に。

最新の FirefoxGoogle ChromeSafariOpera など、:target 疑似クラスに対応したブラウザであれば問題なく動作するはず。IE は IE9 以降のみ。その他、スマートフォンなどでは動作に問題ないと思います。

HTML はほぼ変更なし

HTML 部分は先のエントリーのサンプルと同じですが、1つだけ div 要素を足します。<div class="contents"> の内側に <div class="inner"> を足しています。

下記に、全ソースコードを。

<!-- ↓ここからサンプル↓ -->
<div class="sample">
 
 <div id="tab01">
  <div id="tab02">
   <div id="tab03">
 
    <!-- ↓タブ↓ -->
    <ul id="tab">
     <li><a href="#tab01">W3C</a></li>
     <li><a href="#tab02">XHTML</a></li>
     <li><a href="#tab03">CSS</a></li>
    </ul>
    <!-- ↑タブ↑ -->
 
    <div class="contents">
    <div class="inner">
 
     <!-- ↓タブ 1 の内容↓ -->
     <div class="tab01">
      <section>
       <h1>World Wide Web Consortium</h1>
       <p>...省略...</p>
      </section>
     </div>
     <!-- ↑タブ 1 の内容↑ -->
 
     <!-- ↓タブ 2 の内容↓ -->
     <div class="tab02">
      <section>
       <h1>XHTML</h1>
       <p>...省略...</p>
      </section>
     </div>
     <!-- ↑タブ 2 の内容↑ -->
 
     <!-- ↓タブ 3 の内容↓ -->
     <div class="tab03">
      <section>
       <h1>Cascading Style Sheets</h1>
       <p>...省略...</p>
      </section>
     </div>
     <!-- ↑タブ 3 の内容↑ -->
 
    <!-- .inner --></div>
    <!-- .contents --></div>
 
   </div>
  </div>
 </div>
 
<!-- .sample --></div>
<!-- ↑ここまでサンプル↑ -->

CSS は今回も簡単

先のサンプルでは、position: absolute; で全部を重ねて、あとは z-index / opacity プロパティを使用して表示を切り替えましたが、今回は各タブの内容を float プロパティで横並びにしといて、簡単に言えばタブのクリックごとに各内容をスライドさせて表示させます。

下記のような感じで、下準備。

.sample .contents {
  border: 1px solid #333;
  overflow: hidden; /*(3)はみ出た部分は非表示に*/
  width: 450px; /*(2)タブの内容の横幅に合わせる*/
}
.sample .contents .inner {
  width: 1350px; /*(4)タブの内容の横幅×タブの数*/
}
.sample .inner div { /*これが各タブの内容部分*/
  background: #fff;
  float: left;
  margin: 0;
  padding: 0;
  width: 450px; /*(1)タブの内容の横幅*/
}

コメントを読んでもらえればわかると思いますが、下記の図のように、各タブの内容を横並びにしておいて、1つ分のタブ以外は表示領域外に出して見えなくしてしまいます。

各タブの内容は横並びになっており、1つ分のタブ以外は表示領域外にはみ出しているので見えません

次にタブの切替に関係する部分ですが、該当箇所の CSS は下記のような感じ。

/* タブの表示切替関連 */
.sample .inner div {
  opacity: 0;
}
.sample .inner div:first-child {
  opacity: 1;
}
.sample #tab li:first-child a {
  background: #333;
  color: #fff;
}
.sample #tab02:target .inner .tab02 {
  margin-left: -450px;
  opacity: 1;
}
.sample #tab03:target .inner .tab03 {
  margin-left: -900px;
  opacity: 1;
}
.sample #tab01:target .inner div:not([class="tab01"]),
.sample #tab02:target .inner div:not([class="tab02"]),
.sample #tab03:target .inner div:not([class="tab03"]) {
  margin-left: 0;
}
.sample #tab01:target #tab li a[href$="tab01"],
.sample #tab02:target #tab a[href$="tab02"],
.sample #tab03:target #tab a[href$="tab03"] {
  background: #333;
  color: #fff;
}
.sample :not([id="tab01"]):target #tab li a[href$="tab01"] {
  background: #fff;
  color: #22aacc;
}

ページが読み込まれた時点でのスタイルは先のサンプル同様、下記のようになります。これは、フラグメント識別子がついていない状態で、ページが表示されることを想定しています。

/* まず、すべてのタブの内容を一旦見えなくしてしまいます */
.sample .inner div {
  opacity: 0;
}
/* 初期状態で tab01 がアクティブになる想定で、そのタブが選択されている状態のスタイルを指定します。
   汎用性を考えて、:first-child 擬似クラスで指定しています */
.sample #tab li:first-child a {
  background: #333;
  color: #fff;
}
/* 同じく tab01 の内容も表示します。z-index プロパティで重ね順も一番上にしておきます */
.sample .inner div:first-child {
  opacity: 1;
}

タブをクリックした際の各スタイルは下記のようになります。

/* 「#tab02 がターゲットになっているとき、.tab02 の margin を
    -450px(つまり左に 1つ分ずらす)とすることで .tab02 を表示領域内に移動」する指定 */
.sample #tab02:target .inner .tab02 {
  margin-left: -450px;
  opacity: 1;
}
/* .tab03 は 2つ分ずらさないといけないので -900px */
.sample #tab03:target .inner .tab03 {
  margin-left: -900px;
  opacity: 1;
}
/* タブの内容について「#tab01 がターゲットになっているとき、
   .tab01 以外は margin を元に戻す(初期状態の位置へ)」などそれぞれ指定 */
.sample #tab01:target .inner div:not([class="tab01"]),
.sample #tab02:target .inner div:not([class="tab02"]),
.sample #tab03:target .inner div:not([class="tab03"]) {
  margin-left: 0;
}
/* ↑ここまでで、アクティブなタブの内容は表示領域内に移動してきて表示され、それ以外は非表示になる */
 
/* 次にタブの方。「#tab01 がターゲットになっているとき、
   href="tab01" をもつ a 要素の背景/文字色を変更する」などそれぞれ指定 */
.sample #tab01:target #tab li a[href$="tab01"],
.sample #tab02:target #tab a[href$="tab02"],
.sample #tab03:target #tab a[href$="tab03"] {
  background: #333;
  color: #fff;
}
/* 初期状態でアクティブなタブだけ「#tab01 がターゲットなっていないとき、
   href="tab01" をもつ a 要素の背景/文字色を変更する」指定をしてアクティブ状態を解除する */
.sample :not([id="tab01"]):target #tab li a[href$="tab01"] {
  background: #fff;
  color: #22aacc;
}

以上です。ソースコード自体は今回もとても簡単ですね。

実際に動作するサンプルは下記で確認できます。

課題とか

  1. 各タブのリンクターゲットがタブの内容を含む要素になっていないため、純粋なページ内リンクとして機能しない
  2. 上記 (1) を解決するには各タブの内容(div class="tab01" など)の方に id 名を振ってやればいいけど、それをすると、アクティブなタブのスタイルを指定したりするのが今のところ CSS だけだと無理。あと、タブの内容が多い (高さがある) と、タブをクリックする度にページが内容部分上端までスクロールするのでタブが画面外に消えちゃう

ま、今回の一連のサンプルは、タブ切替 UI を CSS だけで実装するならどうするかな~ ってことでやってみた実験ですので、こんな感じでやれば実現できますねっていうサンプルにはなると思いますが、実際には JavaScript でやった方が色々楽ですし、ブラウザごとの差も埋めやすいので現実的かなと思います。

ということで、あとは下記にサンプルのファイル一式を置いてありますので、適当にいじってみてください。

関連エントリー

Recent Entry

全ての記事一覧を見る

Hot Entry

逆引きおすすめエントリー