何度やっても同じ

ただの日記

counter-resetはスコープを構成する

連番を表示したくて下のようなCSSを書いたのですが、各行の先頭すべてに "1." と表示されてしまい、「???」となったので調べました。

<style>
#list li:before {
  counter-increment: item;
  content: counter(item) ".";
}
</style>
<ul id="list">
  <li>ひー</li>
  <li>ふー</li>
  <li>ほえー</li>
</ul>

とりあえずさっきのリストの各行に正しい番号を(上から順に "1.", "2.", "3." と)表示したければ、次のようなスタイル定義を追加すればいいわけですが、couner-resetプロパティが単にカウンターを0にリセットするだけではなさそうというのがこのことから見えてきます。

#list {
  counter-reset: item;
}

というわけで仕様確認しました

http://www.w3.org/TR/CSS21/generate.html#scope

カウンターのスコープ

あるカウンターのスコープは、ドキュメント内で最初にそのカウンターをcounter-resetした要素から始まり、その子孫要素と兄弟要素そして兄弟の子孫まで含まれる。

子孫要素がスコープとなるのはさっきのリストの例でよくわかります。兄弟もスコープに含まれるので、たとえば次のようなこともできますが、こんなことしないでリスト使えって感じです。だいたいの場合はcounter-resetするのに適切な親要素がありそう。兄弟要素がスコープに含まれることを説明できるもっと適切な例ないかな。

<style>
section h2 {
  counter-reset: item;
}
section p:before {
  counter-increment: item;
  content: counter(item) ".";
}
</style>
<section>
  <h2>メリット</h2>
  <p>お手軽</p>
  <p>安価</p>
  <h2>デメリット</h2>
  <p>太る</p>
  <p>イカ臭くなる</p>
</section>
スコープがネストする場合

ただし、そのスコープ内に同じ名前でカウンターを作成(つまりcounter-reset)している要素がある場合、その要素のスコープは親スコープから除外する。

たとえば次のようにリスト構造がネストしている場合、counter-reset:itemは2回実行されることになりますが、外側のulでリセットされたカウンター「item」と、内側のulでリセットされた同名のカウンターは別物で、それぞれ独自のスコープを構成します。

<style>
ul {
  counter-reset: item;
}
ul li:before {
  counter-increment: item;
  content: counter(item) ".";
}
</style>
<ul>
  <li>aaa</li>
  <li>bbb</li>
  <li>
    <ul>
      <li>child1</li>
      <li>child2</li>
    </ul>
  </li>
  <li>ccc</li>
</ul>

f:id:xfan:20121209155500j:plain

スコープ外でいきなりカウンターを参照した場合

どのcounter-resetのスコープにも含まれない要素でcounter-incrementなどを使ってカウンターを参照しようとした場合、その直前でcounter-resetがカウンタースコープを定義し、値を0にリセットしたように(ブラウザは)ふるまう(べき)。

最初の例で、リストの全行のカウントが1になってしまったのは、このルールによるものですね。