最近まで :nth-child
と
:nth-of-type
の違いについて知らなかった.
というか,:nth-of-type
はどこかで見たことある〜程度で使ったことがなかった.
知らなくても CSS なんとなく大丈夫でしょ! みたいな気分で生きてきた.
だけど
この間,動的に追加されたスタイルの :nth-child
が自分の想像した感じで要素にあたってなくて,
なぜだろうってなったので調査する必要が出てきたところから,
昔読んだ DOM についての記事につながりだした.
表があったとして:
<table class="container"> <tr id="some-tr"> <th>A</th> <td>B</td> <td>C</td> <td>D</td> <th> <table> <tr> <td>E</td> </tr> </table> </th> </tr> </table>
<td>
の 1, 2, 3 番目で色をそれぞれ変えたいみたいな思いがあった:
:nth-child
編
なので,td:nth-child
をざっくり使うと,こうなる:
$colors: ('red', 'green', 'blue', ); .container #some-tr { @each $color in $colors { $index: index($colors, $color); & td:nth-child(#{$index}) { &::before { content: 'td:nth-child(#{$index})'; } & { border: medium solid #{$color}; } } } }
実際のところ,B
, C
, E
にスタイルがあたるようになっている.
理由
これは 2 つ理由があって:
td:nth-child
は まず:nth-child
な要素があって,それをtd
で絞りこまれているE
の<td>
は 直近の<table>
について見れば,td:nth-child(1)
であるということ
なので,ぱっと見 1 番目の <td>
っぽい A
にはスタイルはあたらず,
ぱっと見 5 番目 かつ <th>
の E
に 1 番目のスタイルがあたるようになっている.
:nth-of-type
編
なので,意図した動作のためには,:nth-of-type
と >
を使って こう書く必要がある:
$colors: ('red', 'green', 'blue', ); .container #some-tr { @each $color in $colors { $index: index($colors, $color); & > td:nth-of-type(#{$index}) { &::before { content: 'td:nth-of-type(#{$index})'; } & { border: medium solid #{$color}; } } } }
ほか
ほかにも検証してみた
:nth-child(1)
とかユニークで 1 つしか返ってこないでしょ,みたいな気分で,
document.querySelector
なんかした日には意図しない要素は返ってくるわ,
querySelctorAll
したら複数返ってくるわで意味不明なことになったのだった.
なぜ
なぜ混乱したのかというと,
自分が多くの擬似クラスは前から後ろに絞り込んでいくように思い込んでいたところに,
E:nth-child
が :nth-child
→ E
みたいな絞り込みかたになっているからでは,と思った.
仕様で E:pseudo-class
は E
→ :pseudo-class
の順だよ,とか決まってるのかは知らないし,
確かに目的によってノードの枝刈りの仕方を変えるのは効率的そう.
たとえば p:emptry
なんか DOM 上にある全部の <p>
を走査してから :empty
な要素を見つけるよりも,
:empty
な要素はたいていのケースで少なそうなので,
もし empty
な要素のテーブルを持っているなら,
そこから走査するほうが早いかもしれない……
とか,考えついたときに,昔読んだ Qiita の記事を思い出した:
昔,正直 BEM ってどうなんだろう,って自分の中で思ってたが, この記事を見てなるほどってなったのだった.
今見たら更新があって,他の方の記事も紹介されていて面白い:
Chrome というか Chromium の実装から紐解いてるのが面白い.
整理すれば確かにそうだねってなったので,気をつけたい次第.