内包要素の数が変動しても僕には擬似クラスと間接セレクタがある
- 公開日
- タグ
- Advent Calendar
- CSS
CSS Property Advent Calendar 2013 4日目のエントリです。
昨日、げこたんさんに BEM Advent Calendar を手伝ってもらったら、
おや?プロパティの方に恩返しがありませんね?
— げこたん (@GeckoTang) 2013, 12月 2
と言われてしまったので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)
と ~
に対応しているブラウザは
- Internet Explorer 9+
- Firefox 4+
- Google Chrome 9+
- Safari 5+
- Opera 10+
モダンブラウザなら戦えそうですね!
今気づいたんですけど、CSS Property Advent Calendar なのに CSS セレクタのことを書いてしまいました。まぁいいかな?と思って Advent Calendar を改めて見てみると......。
CSS のプロパティに関することだったら何でも OK です!
セレクタ、ダメです!
5日目はmaechabinさんです。よろしくお願いします。