モードの変更

内包要素の数が変動しても僕には擬似クラスと間接セレクタがある

Advent Calendar
CSS

CSS Property Advent Calendar 2013 4日目のエントリです。

昨日、げこたんさんに BEM Advent Calendar を手伝ってもらったら、

と言われてしまったので2回目を登録しました。

要件

「データがある時はリンクを出したい」などのニーズで内包要素の数がページによって増えたり減ったりすること、けっこうありますよね。それが普通のテキストリンクではなくタブだとかサムネだとかでレイアウトにも関わるとき、要素の数によってスタイルを切り分けなきゃいけないわけですが、タブが2つの時には .tabs2。5つの時には .tabs5 とかいちいちクラス付与させるのも面倒くさいわけです。

どういうことかというと、このような HTML があったとして、

<div class="tabs">
  <a class="tabs__item">タブ1</a>
  <a class="tabs__item">タブ2</a>
  <a class="tabs__item">タブ3</a>
</div>

.tabs__item がページによっては2つだったり5つだったりする時、という想定です。図示すると以下のような感じです。

タブの数が異なるけど親要素内に収まるようにしたい

親の .tabs は幅が決まっていて、操作性を考えて .tabs__item の大きさは常にいっぱいまで広げておきたいな〜とかいう時、擬似クラスと間接セレクタを使うと内包要素の数ごとにクラスを作らなくて済むようになります。

擬似クラスと間接セレクタの合わせ技

.tabs {
  box-sizing: border-box;
  padding: 0 10px;
  width: 320px;
}

.tabs__item {
  box-sizing: border-box;
}

/* 2 tab */
.tabs .tabs__item:first-child:nth-last-child(2),
.tabs .tabs__item:first-child:nth-last-child(2) ~ .tabs__item {
  width: 50%;
}

/* 3 tab */
.tabs .tabs__item:first-child:nth-last-child(3),
.tabs .tabs__item:first-child:nth-last-child(3) ~ .tabs__item {
  width: 33.3%;
}

/* 4 tab */
.tabs .tabs__item:first-child:nth-last-child(4),
.tabs .tabs__item:first-child:nth-last-child(4) ~ .tabs__item {
  width: 25%;
}

/* 5 tab */
.tabs .tabs__item:first-child:nth-last-child(5),
.tabs .tabs__item:first-child:nth-last-child(5) ~ .tabs__item {
  width: 20%;
}

ミソは :first-child:nth-last-child(n) の部分です。:first-child は「最初の子要素」という意味ですね。:nth-last-child(n) は「最後からn番目の子要素」という意味です。:nth-last-child(n) は、たとえば子要素が全部で3つあった時、最初の子要素が :nth-last-child(3)、2番目の子要素が :nth-last-child(2)、3番目の子要素は :nth-last-child(1) となっていきます。先の HTML で言うと、

<div class="tabs">
  <div class="tabs__item">:nth-last-child(3)</div>
  <div class="tabs__item">:nth-last-child(2)</div>
  <div class="tabs__item">:nth-last-child(1)</div>
</div>

こうですね。.tabs__item が5つの時は

<div class="tabs">
  <div class="tabs__item">:nth-last-child(5)</div>
  <div class="tabs__item">:nth-last-child(4)</div>
  <div class="tabs__item">:nth-last-child(3)</div>
  <div class="tabs__item">:nth-last-child(2)</div>
  <div class="tabs__item">:nth-last-child(1)</div>
</div>

こういうことになります。で、これに :first-child を重ねるとどうなるかというと、:first-child は子要素の数にかかわらず1番目にマッチするので

<div class="tabs">
  <div class="tabs__item">:nth-last-child(3):first-child</div>
  <div class="tabs__item">:nth-last-child(2)</div>
  <div class="tabs__item">:nth-last-child(1)</div>
</div>

<div class="tabs">
  <div class="tabs__item">:nth-last-child(5):first-child</div>
  <div class="tabs__item">:nth-last-child(4)</div>
  <div class="tabs__item">:nth-last-child(3)</div>
  <div class="tabs__item">:nth-last-child(2)</div>
  <div class="tabs__item">:nth-last-child(1)</div>
</div>

ということになります。擬似クラスの組み合わせで子要素の数に応じてスタイルを切り分けることができました。

しかし、.tabs .tabs__item:first-child:nth-last-child(5) と指定しただけでは .tabs__item が5つのときの最初の要素にしかスタイルが適応されません。そこで間接セレクタ ~ を使います。

例えば .box1 ~ .box2{...} とすると、.box1 と兄弟関係にある弟要素の .box2 がスタイル適用対象となります。

.box1 ~ .box2 {
  background: tomato;
}
<div class="box2">テキスート.box2</div>
<div class="box1">テキスート.box1</div>
<div class="box2">テキスート.box2</div>
<div class="box2">テキスート.box2</div>
<div class="heading">ヘッディーン.heading</div>
<div class="box2">テキスート.box2</div>

上記のような HTML の場合、3行目の .box2 からが適応対象です。1行目の .box2.box1 の兄弟関係にありますが、.box1 より先に記述されており弟要素でないのでスタイル適応外となります。また、途中に .heading が挟まっていすが、そのあとに出てくる .box2 は先の .box1 の弟要素なのでスタイルは適応されます。CodePenにデモを置いたので参考にどうぞ。

間接セレクタで間にある兄弟要素をカバーしたら勝ったも同然

.tabs の HTML に戻りましょう。

.tabs .tabs__item:first-child:nth-last-child(5) ~ .tabs__item { ... }

これを見ると、最後から5番目かつ最初の .tabs__item の兄弟要素な .tabs__item となります。これだと .tabs__item:first-child にはスタイルが適応されないので、.tabs__item:first-child:nth-last-child(5) をグルーピングして同じルールセットを適用させます。

.tabs .tabs__item:first-child:nth-last-child(5),
.tabs .tabs__item:first-child:nth-last-child(5) ~ .tabs__item { ... }

というわけです。:nth-last-child(n) の値を変えれば内包要素が何個になっても対応できます。便利ですね!

で、:nth-last-child(n)~ に対応しているブラウザは

モダンブラウザなら戦えそうですね!


今気づいたんですけど、CSS Property Advent Calendar なのに CSS セレクタのことを書いてしまいました。まぁいいかな?と思って Advent Calendar を改めて見てみると......。

CSS のプロパティに関することだったら何でも OK です!

[CSS Property Advent Calendar 2013 - Adventar](http://www.adventar.org/calendars/57)から

セレクタ、ダメです!

5日目はmaechabinさんです。よろしくお願いします。