2013年1月31日木曜日

cssセレクタの説明

cssセレクタについて、今まで参考になりそうなサイトをサラッと流し読みしたくらいでマジメに調べたことがなかったので調べてみました。 css3の情報がメインです。 htmlで使用する場合について書きました。 ブラウザ以外のアプリケーションでもcssが使われることはありますが、そちらについては考慮してません。 名前空間については調べてません。

  1. 確認環境とその日時
  2. 構文の概要
    1. 単体セレクタシーケンスと結合子
    2. セレクタのグループ化
  3. 単体セレクタ1 / 基本的なセレクタ
    1. 型セレクタ / 要素名での絞り込み
    2. 全称セレクタ / 要素名での絞り込みはしない
    3. クラスセレクタ / .classでの絞り込み
    4. idセレクタ / #idでの絞り込み
    5. 属性セレクタ / [属性]での絞り込み
  4. 単体セレクタ2 / 構造擬似クラス
    1. :nth-child() と :nth-of-type() / 順番による絞り込み
    2. その他の順番による絞り込み
    3. :only-child / 一人っ子かどうかで絞り込み
    4. :only-of-type / 兄弟に同じ要素名がないかで絞り込み
    5. :empty / 要素が空かどうかで絞り込み
    6. :root / ルート要素かどうかで絞り込み
  5. 単体セレクタ3 / 論理擬似クラス
    1. :not() / 除外
    2. :matches() / どれかに一致
  6. 単体セレクタ4 / リンク関係の擬似クラス
    1. :link と :visited / 未訪問か訪問済みかで絞り込み
    2. :any-link / 未訪問でも訪問済みでも
    3. :target / ページ内リンクが利用された
    4. :local-link / 内部リンクかで絞り込み
  7. 単体セレクタ5 / UIに関する擬似クラス
    1. :hover / ポインティングデバイスが乗ってるかで絞り込み
    2. :active / 要素がアクティブかで絞り込み
    3. :focus / フォーカスがあるかで絞り込み
    4. :checked / ラジオボタンやチェックボックスで絞り込み
    5. :indeterminate / :checkedが確定していない場合
    6. :enabled と :disabled / UIの有効無効で絞り込み
  8. 単体セレクタ6 / その他の擬似クラス
    1. :lang / 言語による絞り込み
  9. 擬似要素
    1. ::first-line 擬似要素
    2. ::first-letter 擬似要素
    3. ::before 擬似要素と ::after 擬似要素
    4. ::selection 擬似要素
  10. 結合子 / 位置関係で要素をたどる
  11. その他のメモ
    1. 結合子*の勘違い
    2. css4で提案されているセレクタの対象決定

・確認環境とその日時

この投稿に書いた内容は次のブラウザで確認しています。

  • firefox 18
  • google chrome 24
  • opera 12
  • internet explorer 9

osはwindows7 home 64bitです。

確認日時は2013年の1月後半です。 文中に現時点とある場合はそのへんの日付を表しています。 書くのに多少手間取ったので、書いている間にcss4の情報などが変わっているかもしれません。

・構文の概要

単体セレクタシーケンスと結合子

セレクタは単体セレクタシーケンスと結合子の組み合わせでできています。 例えば、次のようなセレクタの場合、

.contents[title^="p"] > table.sample tr.data:nth-of-type(2)

単体セレクタシーケンスを青、結合子を赤で示すとこうなります。

.contents[title^="p"] > table.sampletr.data:nth-of-type(2)

単体セレクタシーケンスは要素リストの絞込みをする部分です。 結合子はそれまでのマッチングでできた要素リストの個々の要素から子孫や兄弟をたどって新たな要素をリストアップし、新たな要素リストを作る部分です。 単純に左から読みます。 さかのぼる事はありません。 空白文字も結合子なので、単体セレクタシーケンスに空白文字を含めることはできません(括弧や文字列の中は可)。 結合子の前後には空白文字がいくつあってもokです。 インデントなどの整理をするなら結合子のところでやりましょう。

単体セレクタシーケンスについてもう少し補足します。 これは個別の単体セレクタを連ねたものです。 単体セレクタシーケンスの最初は必ず要素(タグ)名で絞り込む型セレクタか、要素名による絞込みを省略することを表す全称セレクタになります。 以降、属性や要素の順番、要素の状態などで絞り込むセレクタが続きます。

セレクタの最初の単体セレクタシーケンスはドキュメントにある全ての要素を対象に絞り込みをします。 2つ目以降の単体セレクタシーケンスは、直前の結合子が用意した要素リストを対象に絞り込みをします。

単体セレクタシーケンスについて、css3では「sequence of simple selectors」と呼ばれており、css4では「compound selector」と呼ばれています。 「compound selector」を「合成セレクタ」と訳しているところもあるようですが、それが正式な呼称として普及するかどうかは不明です。

セレクタのグループ化

同じスタイルを適用するセレクタがいくつかある場合、コンマで区切ってひとまとめにできます。

p.info{ color:blue; }
div#author{ color:blue; }
h1{ color:blue; }

 ↓

p.info, div#author, h1{ color:blue; }

グループ化したセレクタ全体が示す対象要素リストは、個々のセレクタの対象要素リストの和集合になります。

セレクタの基本動作は絞り込みです。 結合子で要素数が増えることはありますが、親要素をたどることはできないので、これも文書構造の深さでの絞込みと言えます。 よって、スタイルの適用範囲を増やすにはセレクタを増やしてグループに追加するしか方法はありません。

・単体セレクタ1 / 基本的なセレクタ

単体セレクタには実際にドキュメントに書かれた情報を元に絞り込みを行う基本的なセレクタと、実際にはドキュメントに書いてない情報(ドキュメント上の順番や閲覧者の操作するマウスカーソルの位置など)を元に絞り込みを行う擬似クラスがあります。 ここでは基本的なセレクタについて説明します。

この項目ではこんなhtmlを対象にどんなセレクタを書いたらどこに効くのかを確認していきます。

・サンプル1

textareaはコピー&ペースト用ということでこのサイズにしときます。 firefoxで表示したのをキャプチャするとこんな感じになります。

サンプル1のキャプチャ
  • 別ページで実際の表示を確認 ... データURIスキームで書かれているため、特定のブラウザでしか表示できません。 firefox、google chrome、operaで確認しました。 ieは10以降で見れるらしいけど未確認です。 以降の確認ページも全て同様です。

このサンプルは何度か使いまわします。 使いまわしたとき、セレクタがどこに効くかを確認することもあるため、ちょっと行儀が悪いけど全要素divにしてあります。 本来は要素の型と用途が合うようにheaderはh1、contentはp、footerもpにするのが適切です。 全部のdiv要素は「border:solid 1px black;」にしてレイアウトが分かるようになっています。 div開始タグのすぐ後には属性が書いてあります。 class属性があるなら「.クラス名」、id属性があるなら「#id」と表示してあります。 セレクタの確認用という事で細かいところは気にしてません。 段組のスタイルとかは適当です。

型セレクタ / 要素名での絞り込み

要素名での絞り込みです。 単体セレクタシーケンスの先頭にしか書けません。

サンプル1ではdiv要素にボーダーや余白をつけるために使っています。

div {
    margin:5pt;
    border:solid 1px black;
}

要素名での絞り込みをしない場合省略することが可能で、そのときは後述の全称セレクタを指定したことになります。

全称セレクタ / 要素名での絞り込みはしない

単体セレクタシーケンスの先頭にしか書けません。 型セレクタで要素名を書く代わりに全称セレクタを使用すると要素名による絞り込みは省略されます。 全称セレクタの記号はアスタリスク「*」です。 これは影響範囲が大きいので、その他の絞込みと組み合わせて使うことがほとんどでしょう。

言い換えると「何もしない」ということなので多くの場合記述を省略できます。 いきなり他の単体セレクタから書いてもokです。 ただし、単体セレクタシーケンスには最低1つの単体セレクタが無ければなりません。 結合子の使い方次第では「どんな要素でも1つあれば良い」というケースもでてきます。 そのような時は、何かセレクタを書かなければならないため、全称セレクタを省略せずに書くことになります。

結合子に空白文字が使われているという都合上、全称セレクタを省略すると読みにくくなってしまう場合があります。 全称セレクタを省略するかどうかは読み易さを考慮して適宜判断しましょう。

クラスセレクタ / .classでの絞り込み

要素にclass属性が付いている場合、「.class名」とドットで区切って書けば絞り込めます。 例えば <p class="subtle">微妙</p> という要素を指定するには「p.subtle」と書きます。

サンプル1ではentryの背景色の指定などで使っています。

.entry{
    background-color:white;
}

class属性は要素の分類をするためにあるので、1つのhtmlドキュメントで同じclassを何度も使うことができます。 1つのclass属性に半角スペースで区切られた複数の値を持たせることも可能です。 例えば、 <p class="salty sweet">甘辛い</p> と書かれていたら「.salty」と「.sweet」両方のセレクタにマッチします。

idセレクタ / #idでの絞り込み

要素にid属性が付いている場合、「#id名」と#で区切って書けば絞り込めます。 例えば <p id="unintelligible">難解</p> という要素を指定するには「p#unintelligible」と書けます。 id属性はドキュメント中に一意という前提があるため、多くの場合他の単体セレクタや結合子の後に書くのは無駄です。 絞り込みに意味があるときを除いて、単に「#id名」と書いたほうがいいでしょう。 この場合は「#unintelligible」と書いた方が効率的です。

サンプル1ではレイアウトのための要素left、center、rightのスタイルを書くために使っています。

#left {
    background-color:lightcyan;
    float: left;
    width: 100pt;
}

これらの要素は1ページに1つしかないのでclassにする必要はありません。 1ページに1つしかないというルールなので、ブラウザはclassで要素を探すよりもidで探す方が速いはずです。

ちなみに、同じidの要素がいくつもある場合についてはどういう動作になるかはブラウザ次第です。 もし間違った書き方をして今思ったとおりに表示されたとしても、今後もそれが続くという保障はありません。

属性セレクタ / [属性]での絞り込み

要素の属性で絞り込みをします。

  • [属性名] ... 属性がある。中身不問。
  • [属性名 = "値"] ... 属性値と値が一致。
  • [属性名 ^= "値"] ... 属性値の先頭が値から始まる。
  • [属性名 *= "値"] ... 属性値に値が含まれる。
  • [属性名 $= "値"] ... 属性値の最後が値で終わる。
  • [属性名 ~= "値"] ... 半角スペースで区切られた属性値の1つ。
  • [属性名 |= "値"] ... ハイフン-で区切られた属性値の最初のもの。

classやidもこの書き方で指定することができます。 しかし、ブラウザの絞り込みルーチンはよく使われるclassやidについて最適化されているかもしれません。 普通はクラスセレクタ.やidセレクタ#を使った方がいいでしょう。

使用例として、 サンプル1に次のスタイルを追加したサンプル2を示します。

div[lang]{
    text-decoration:line-through;
}
div[id = "l1"]{
    background-color:yellow;
}
div[title ^= "abc"]{
    background-color:red;
}
div[title *= "ij"]{
    background-color:blue;
}
div[title $= "pqr"]{
    background-color:green;
}
div[class ~= "second"]{
    color:white;
}
div[title |= "stu"]{
    font-size:large;
    font-style:italic;
}

・サンプル2

サンプル2のキャプチャ

・単体セレクタ2 / 構造擬似クラス

擬似クラスはドキュメントに書かれていない情報を元に絞り込みをするための単体セレクタです。 コロン:から始まります。

その中でも構造擬似クラスはマッチング対象の要素の文書構造を元に絞り込みをします。

:nth-child() と :nth-of-type() / 順番による絞り込み

マッチング対象の要素が兄弟要素の中で何番目にあるのかを元に絞り込みをします。 「それまでの絞り込みで得られた要素の中で何番目か?」では無いので注意。 あくまでも絞り込みなのでマッチング対象の要素のみを見ます。 マッチング対象の要素の子を見に行くようなことはありません。 特に :nth-child() は勘違いし易いので注意しましょう。

ちなみに、「それまでの絞り込みで得られた要素の中で何番目か?」というセレクタはcss4で :nth-match() として提案されていますが、現時点でそれを実装しているブラウザは無いようです。

これらの擬似クラスは引数を1つとります。 :nth-child(引数) は兄弟要素全部で通し番号を付けて、引数と番号が一致するかを判定します。 :nth-of-type(引数) は兄弟要素の中で同じ要素名のものだけで通し番号を付けて、引数と番号が一致するかを判定します。 要素のみを数えるため、テキストノードなどの要素以外のDOMノードは対象外です。 単純に番号の比較をするだけで、その他の処理はありません。

単純化した例を挙げると、

<p>
    <span>1番目の子</span>
    <span>2番目の子</span>
    テキストノードは無視
    <span>3番目の子</span>
    <a>4番目の子 - a要素の中では1番目</a>
</p>

というような番号と引数との比較になります。 これはブラウザがhtmlファイルを読み込んだときに付く番号だと思っておきましょう。 (正確にはドキュメントツリーを作ったときの番号。もしくは絞り込みが発生するまで数えられないかもしれない。) ドキュメントの変更が無い限り、番号が変わることはありません。

引数には

  • 数字
  • 3n+1のような式での指定
  • 偶数ならeven、奇数ならodd

の3通りの指定ができます。

数字での指定はそのままです。 基数は1、つまり最初の要素を指定したいときは :nth-child(1) になります。 多くのプログラム言語のように0ではないので注意。

式での指定について、複雑な指定はできません。 an+b、-an+b、an-b、-an-bの形式でのみ指定できます。 aとbに指定できるのは0以上の整数です。 3n+1、4n-1、-2n+8のような形になります。

aの前にマイナスが付くときは、0≦n で式全体の値が0より大きくなるときのみマッチします。 aがプラスのときについてはcss3の仕様書には記述はありませんが、私の試したブラウザでは an+b のaとb両方が正で、さらに a<b の場合、b番目より通し番号が小さい要素はマッチしませんでした。 つまり、例えば 2n+4 を指定したとき1~3番目の要素はマッチしませんでした。 この場合でも多くのブラウザで、nの範囲は0以上で判定されているものと思われます。

式には空白文字を書くことができます。 ただし、aとその前の符号は離してはいけません。

evenまたはoddの指定について、以下の式で指定したときと同じ処理になります。

  • even = 2n
  • odd = 2n+1

evenとoddは文字列扱いではないのでクォーテーションで囲む必要はありません。

一通り説明したのでもう1つ例を挙げておきましょう。 以下のサンプルは、

  • iの後の数字は子要素全てでの通し番号、偶数のとき背景色を赤にする。
  • aの後の数字はa要素の通し番号、偶数のときボーダーを太枠にする。
  • sの後の数字はspan要素の通し番号、偶数のときボーダーを破線にする。

となっています。

・サンプル3

サンプル3のキャプチャ
その他の順番による絞り込み

次の擬似クラスについて説明します。

  • :nth-last-child()
  • :nth-last-of-type()
  • :first-child
  • :first-of-type
  • :last-child
  • :last-of-type

概要については前の項目を見てください。

:nth-last-child() は通し番号を兄弟要素の最後から振る以外は :nth-child() と同じです。 :nth-last-of-type() も通し番号を兄弟要素の最後から振る以外は :nth-of-type() と同じです。

first-child は :nth-child(1) と同じです。 first-of-type は :nth-of-type(1)と同じです。

:last-child は :nth-last-child(1) と同じです。 :last-of-type は :nth-last-of-type(1) と同じです。

:only-child / 一人っ子かどうかで絞り込み

兄弟要素が無い場合マッチします。 要素以外のノードは無視されます。 つまり、テキストノードも無視されます。 そのため、

<p>
    テキストノード<br />テキストノード
</p>

とある場合はbr要素はonly-childです。

:only-of-type / 兄弟に同じ要素名がないかで絞り込み

兄弟要素に同じ要素名のノードが無い場合マッチします。 後は前の項目と同じです。

:empty / 要素が空かどうかで絞り込み

子ノードを持たない場合マッチします。 <hr /> のように終了タグを省略している場合はもちろんマッチ。 終了タグを書く場合は <div></div> のように開始タグと終了タグの間に空白文字が入っていない場合有効です。 スペース1つでも入っていたらテキストノードになるので無効。 属性は有ってもokです。

私の試したブラウザでは、<div/>のように空要素の終了タグを省略してはならないのに省略してしまった場合、マッチしませんでした。 ブラウザのパースの問題かもしれませんが詳細不明です。

:root / ルート要素かどうかで絞り込み

ルート要素の場合マッチします。 htmlドキュメントのルート要素はhtmlです。 ブラウザのパーサの仕組み的にはセレクタでhtmlと書くよりこっちの方が速い可能性はありますが、効果は僅かだし分かりにくいので使うことはないでしょう。

・単体セレクタ3 / 論理擬似クラス

論理擬似クラスというのは私が適当に付けた名前です。 css3の仕様書にはそれっぽい名前がなかったし、css4の仕様書では「Logical Combinations」という項目の中でセレクタのグループ化とごっちゃになってたので適当に名付けました。

論理擬似クラスの入れ子はできないので注意しましょう。

:not() / 除外

:not() 擬似クラスは引数で与えられた単体セレクタに一致するものを除外します。 javaやC#などのコレクションクラスでいうところのremoveメソッドのようなものです。

書式は :not(引数) です。 引数には単体セレクタを渡せます。 単体セレクタシーケンスや、結合子を含めたセレクタを渡すことはできません。 現在の仕様では論理擬似クラスの入れ子はできないので、引数に :not() や :matches() を渡すことはできません。

ちなみに、css4の仕様では、引数はコンマで区切って複数渡せるように拡張されています。 (これはcss3の :not() でも :not(span):not(#right) などのように連ねれば実現できるので、書き方の幅を増やしたというだけでしょう。) もう1つ仕様変更があって、引数に単体セレクタだけでなく単体セレクタシーケンス(css4の記述ではcompound selector)を渡せるように拡張されています。 もちろん、現時点でこれらの仕様変更に対応しているブラウザはありません。

単純化した例で説明します。

span:not(.b){ font-size:x-large; }

...

<p>
    <span class="a">A</span>
    <span class="b">B</span>
    <span class="c">C</span>
</p>

というcssとhtmlがあった場合のセレクタ span:not(.b) に注目しましょう。 まず、 :not() の前の単体セレクタまで処理されると要素リストにはspan.a、span.b、span.cの3つが収められます。 :not() 擬似クラスに処理が移るとこの中から引数 .b にマッチする span.b を除外します。 結果、セレクタ全体が指す対象はspan.aとspan.cになります。

:matches() / どれかに一致

:matches() 擬似クラスはcss4で標準化が提案されています。 というわけでcss3の標準化もボチボチという現在、そのまま実装しているブラウザはありません。 一部のブラウザでベンダー接頭辞付きの別名で実装されています。

  • firefox ... :-moz-any()
  • google chrome ... :-webkit-any()

どのブラウザで見られるか分からない一般公開されたウェブサイトでこれを使うのは現実的ではありません。 しかし、アドオンなどで使う分には役に立ちそうなのでここで挙げておきます。

書式は :matches(引数1, 引数2, 引数3, ...) です。 引数は単体セレクタシーケンス(css4の記述ではcompound selector)です。 :matches() は渡された要素リストの中からいずれかの引数に一致するものを残します。 あくまでも絞り込みなので、渡された要素リストに含まれない要素が出てくることはありません。 例えば、

#main :matches(li, td, .item)

とある場合、#main要素の子孫でli要素、td要素とitemクラスに属する要素が残ります。

#main :matches(li, td, .item) の様子

この擬似クラスを使えばセレクタのグループ化を省くことができるかもしれません。 次のグループ化された2つのセレクタは最後の .header と .footer のところ以外同じです。

#center .entry[title] .header,
#center .entry[title] .footer {
    background-color:#d0d0d0;
}

これは :matches() を使えば次のように書き換えられます。

#center .entry[title] :matches(.header, .footer) {
    background-color:#d0d0d0;
}

閲覧者のウェブサイト閲覧履歴や閲覧中のurl、リンク対象のホストなど、ドキュメントに書かれていない情報を元に絞り込みをします。

アンカータグのリンク先が未訪問か訪問済みかでマッチングをします。 :link は未訪問のリンクにマッチします。 :visited は訪問済みのリンクにマッチします。 訪問済みの情報は一定期間でリセットされることになっています。 リセットのタイミングはブラウザ依存です。

迷惑なことに、cssの製作者はこれを使って閲覧者の訪問情報を収集することができます。 例えば、

#tr_id:visited {
    background-image: url("http://tracking-server.jp/id0339.gif");
}

...

<a href="トラッキングしたいurl" id="tr_id">見てね</a>

とすれば訪問済みのときだけトラッキング画像へのリクエストが送信されるかもしれません(動作未確認)。 そういうトラッキングを無効化するため一部のブラウザでは :visited で指定できるスタイルは制限されているそうです。

css4の擬似クラスです。 「要るか要らないか?」って言われたら要らないんですが流れ的に一応記述。 仕様書のWorking Draft見たら「もっといい名前ない?」って書いてありました。 名前が変わるかもしれません。

hrefを持ったアンカー全てがマッチします。 :link と :visited を組み合わせれば同等のことができます。 「アンカー要素でマッチングした場合、アンカータグにidだけを書いたページ内リンクターゲット用の要素にまでスタイルが適用されてしまう。それじゃ都合が悪い。」って人向けの擬似クラスだとか。

でも今はh1要素とかspan要素とかにidを書いてリンク先にするのがよいhtmlと言われているんで、やっぱり必要ないかと...

ちなみに、現時点で実装しているブラウザはfirefoxとgoogle chromeだけ確認しました。 まだ正式採用ではないようで、ベンダー接頭辞付きの別名で実装されています。

  • firefox ... :-moz-any-link
  • google chrome ... :-webkit-any-link
:target / ページ内リンクが利用された

htmlのアンカーには#を付けてページ内リンクを指定できます。 そのページ内リンク付きのアンカーを踏んでページを閲覧中のとき(url直打ちでもいいですが)この擬似クラスは有効になります。

例えば、 <a href="http://www.w3.org/TR/selectors4/#target-pseudo">css4仕様書の:target</a> と書かれた場合、そのページ内の #target-pseudo というidを持った要素は :target とマッチします。

css4で提案されてはいるけど実装されているブラウザがあるかは知りません。 しかしもし実際に使えたら役に立ちそうなので一応紹介しておきます。

:local-link は引数無しで書いた場合、同じドキュメントへのリンクとマッチします。 引数(0以上の整数)を書いた場合、同じドメインかどうかでマッチングして、さらに数字の分だけディレクトリ階層のマッチングもします。 ドメインとディレクトリ階層だけを見るのでその他の項目(プロトコル、Basic認証などのユーザ名、パスワード、ポート、#以降のフラグメント識別子や?以降のクエリ文字列)は無視されます。

もし実装されたら、移行するかもしれないウェブサイトを書くときに使えそうですね。 ユーザサイドでもリンク先が内部リンクか外部リンクかで表示を変えるユーザスタイルやアドオンなどを作れそうです。

・単体セレクタ5 / UIに関する擬似クラス

閲覧者のマウス操作やフォームの状況など、ユーザインターフェースの状態を元に絞り込みをします。 閲覧者の操作によってマッチ/アンマッチは動的に変わります。

:hover / ポインティングデバイスが乗ってるかで絞り込み

マウスなどのポインティングデバイスのカーソルが要素に乗っていたらマッチします。 cssは後に指定したスタイルの方が優先されるので書く順番が重要になります。 具体的には、リンク関係の擬似クラス:active と併用する場合、

  1. リンク関係の擬似クラス
  2. :hover
  3. :active

の順で書かないと上手く表示されません。 また、セレクタの優先度は同じになるように調整しなければなりません。

:active / 要素がアクティブかで絞り込み

要素がアクティブなときにマッチします。 アクティブというのは抽象的な言葉ですが、例えばマウスのボタンを押してから放すまでの間がアクティブです。 フォームやリンクはもちろん、span要素などにも有効です。

リンクに使ったとき、ブラウザによって閲覧者がドラッグして放すとアクティブなままになったりならなかったりと微妙に違いがあります。 こだわる場合は注意が必要です。

リンク関係の擬似クラス:hover と併用する場合の書き順については :hover の説明を見てください。

:focus / フォーカスがあるかで絞り込み

フォームなどのユーザインターフェースやリンクにフォーカスがあるときにマッチします。 :hover:active とは違い、ユーザインターフェースかリンクにしか使用できません。 フォーカスはクリック、タブキー、アクセスキーなどで切り替わります。

:checked / ラジオボタンやチェックボックスで絞り込み

フォームのラジオボタンやチェックボックスで項目がチェックされているときにマッチします。

:indeterminate / :checkedが確定していない場合

ラジオボタンやチェックボックスで項目がチェックされているわけでもチェックされていないわけでもない不確定な状態のときにマッチします。

この擬似クラスに関しては情報があまりありません。 css3の仕様を見ても、将来のバージョンで採用される予定としか書かれていません。 「indeterminate css」というキーワードで検索しても、数年前のページで「Operaのみ対応」と書かれているのが見つかるだけです。 いくつかのブラウザで実際に試すと、Operaではグループ内にチェックされている項目がないラジオボタンでのみマッチしました。 その他はダメでした。 javascriptでindeterminateに値を設定とかしても無反応。 この擬似クラスが普及するのはまだまだ先のようですね。 もし今同等の事を実現したかったら、javascriptでラジオボタンやチェックボックスを0から作るとかしなければならないかも?

:enabled と :disabled / UIの有効無効で絞り込み

:enabled はフォームなどのユーザインターフェースが有効な場合に、 :disabled は無効な場合にマッチします。 ユーザインターフェースはデフォルトで有効です。

私の試したブラウザでは、要素のenabled属性やdisabled属性が有るかどうかだけが判定の基準でした。 つまり、 <input disabled="false" ...></input> のようにした場合、書いた人の意図はenabledになって欲しいのかもしれませんが、disabled属性があるかないかだけで判定されtrue扱いになりdisabledでした。 ユーザインターフェースはデフォルトで有効なので、javascriptなどで切り替える場合はdisabled属性の有無だけを切り替えればいいのかもしれません。

・単体セレクタ6 / その他の擬似クラス

:lang / 言語による絞り込み

言語による絞り込みをします。 書式は :lang(引数) です。 引数には ja-JP などが入ります。 文字列ではないのでクォーテーションは不要です。

属性セレクタでも [lang |= "???"] のようにして似たようなことが書けますが、これはもっと高機能です。 httpのレスポンスヘッダやhtmlヘッダのmeta要素などを見て、総合的に判断し絞り込みをします。 また、 [lang |= "???"] がlang属性が付いた要素そのものしかマッチしないのに対して、 :lang は子孫の要素でも言語が分かればマッチします。

例えば、 <p lang="ja">明日は<span>肉</span>を食べる</p> というhtmlドキュメントがあった場合、lang属性が書かれたp要素はもちろん、その子孫のspan要素も :lang(ja) にマッチします。

・擬似要素

擬似要素は実際にはドキュメントに書かれていない無い要素をスタイルの対象にするための仕組みです。 コロンを2つ重ねた::から始まります。 旧バージョンとの互換性のためコロン1つで書くこともできますが、新しくcssを書くのなら2つにしておいた方がいいでしょう。

セレクタ全体の最後に書きます。 (グループの最後ではなく、コンマで区切られた個々のセレクタの最後です。) ただし、css4の仕様書には「セレクタの対象決定」に関する構文が書かれているので、それが普及したら書き方が変わるかもしれません。

css3では1つのセレクタに付き1つしか書けませんが、将来2つ以上書けるようになる可能性があるそうです。

::first-line擬似要素

テキストノードの最初の行を要素とみなしてスタイル適用の対象にします。 本来、テキストノードはDOMの木構造のリーフなので要素にはなりえませんが、その1行目をさもspan要素で囲っているかのように扱うことができます。

1行の範囲はビューアによって変わる可能性があり、さらに動的に変化します。 例えば、htmlファイルをブラウザに表示してブラウザのウィンドウサイズを変えれば1行の範囲は変わりますが、1行の範囲が変わっても1行目にだけスタイルが適用されます。 仕様では要素が入れ子になっている場合でも、子孫要素をたどってテキストノードを探し出し、対象要素の1行目にあたる部分にスタイルを適用することになっています。 しかし、現時点ではブロックノードの入れ子についてはブラウザごとに対応状況は違います。

後述の ::before 擬似要素 ::after 擬似要素で文字列が追加された場合、それをスタイル適用の対象に含むことがあります。 注意しましょう。

spanなどのインライン要素が対象要素の子にあり、1行目の途中から始まって数行にわたった場合でも、spanのスタイルと ::first-line で指定したスタイルは両立されます。

適用できるcssプロパティは限られています。 css3の仕様で適用することになっているのはfont系、color系、background系、word-spacing、letter-spacing、text-decoration、vertical-align、text-transform、line-heightです。 ブラウザによってはそれ以外のスタイルに対応しているかもしれません。

::first-letter擬似要素

テキストノードの最初の1文字を要素とみなしてスタイル適用の対象にします。 本来、テキストノードはDOMの木構造のリーフなので要素にはなりえませんが、その1文字目をさもspan要素で囲っているかのように扱うことができます。

子ノードの先頭に画像など他のインライン要素がある場合、それに続くテキストノードの1文字目はスタイル適用の対象になりません。 子ノードの先頭にspanなどのテキストノードを持ったインライン要素がある場合、その1文字目はスタイル適用の対象になります。

数字の最初の桁でも適用されてしまうので、読みにくくなってしまうこともあります。 注意しましょう。 後述の ::before 擬似要素 ::after 擬似要素で文字列が追加された場合、それをスタイル適用の対象に含むことがあります。 注意しましょう。

適用できるcssプロパティは限られています。 css3の仕様で適用することになっているのはfont系、color系、background系、margin系、padding系、border系、text-decoration、text-transform、letter-spacing、word-spacing、line-height、float、vertical-align(floatを使っていないときのみ)です。 ブラウザによってはそれ以外のスタイルに対応しているかもしれません。

仕様では2文字1組の文字でも1文字扱いにするように書かれてますが、現時点ではブラウザごとに対応状況は違います。 例えば、平仮名の「ぱ」1文字で書くこともできますが、「は」(&#12399;)と半濁音(&#12442;)の2文字で書くこともできます。

<p>&#12399;&#12442;ぴぷぺぽ</p>

これを ::first-letter 擬似要素で拾った場合の表示はブラウザによって違います。 また、span要素で区切った場合も対応すべきなんですが、現時点でこれを正常に表示できるブラウザは見つかりませんでした。

<p><span>&#12399;</span>&#12442;ぴぷぺぽ</p>
::before 擬似要素と ::after 擬似要素

対象要素の子ノードリストの先頭/最後に擬似要素を追加します。 イメージ的には次のような扱いになります。

#t::before{ content: "before"; }
#t::after{ content: "after";}

...

<p id="t">ぱぴぷぺぽ</p>

<p><content>before</content>ぱぴぷぺぽ<content>after</content></p>

追加された擬似要素はドキュメントツリーには何も影響を与えません。 表示だけが変わります。 他のセレクタで指定することはできませんが、 ::first-line 擬似要素 ::first-letter 擬似要素には影響を与えます。 擬似要素のスタイルは対象要素から継承されます。 継承ルールは他の要素と同じです。

追加する擬似要素はcssのcontentプロパティで内容を指定できます。 contentプロパティに指定できるものは次のとおりです。

  • 文字列 ... シングルクォーテーションかダブルクォーテーションで囲む。
  • リソース ... 画像などをurlで指定する。
  • カウンタ ... ol > li要素 のようにカウンタを付けることができる。複雑なので省略。
  • クォーテーション ... ブラウザによって見た目が異なる。
  • 属性 ... attrで指定。対象要素の属性を表示する。

contentプロパティにスペースで区切って複数書けます。 分割して書くことはできません。 かち合ってしまったときはスタイルの優先順位が高いものだけが適用されます。

追加された要素は、現状ではie以外のブラウザでは閲覧者の操作で選択ができませんでした。 追加された文字列を閲覧者がコピー&ペーストできるのはieだけです。 閲覧者から見て不自然になるかもしれないので注意。

サンプル1をちょっと書き換えてこの擬似要素の挙動を見てみましょう。 まずはcssに次の項目を追加。

[class]::before {
    content: "." attr(class);
}
.footer::before {
    content: "rewrite" url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEX///8AAABVwtN+AAAALUlEQVQI12P4wcDwg4fhAA/DBzA6AEYcPAwSPAwGPAz1f0DIgIFBgoGBgwEIACwIChPWe3TOAAAAAElFTkSuQmCC);
}

クラス名を本文から消して ::before 擬似要素で出力させるようにします。 .footerだけ ::before 擬似要素での出力を上書きします。 .footerではurlでの画像リソースの指定もしています。 普通のurlでもいいんですが、bloggerに投稿する場合外部リソースを用意するのが面倒だったのでデータURIスキームにしてみました。

・サンプル4

サンプル4のキャプチャ
::selection擬似要素

閲覧者がウェブページのテキスト部分をマウスドラッグやキー入力などで選択した場合、その選択部分を要素とみなしてスタイル適用の対象にします。 フォームのselectコントロールは無関係です。

現在css3の仕様書には削除された項目だと書かれています。 また、css4の仕様書には記述がありません。 削除されたのは、ウェブページの作者に悪意があったときに閲覧者に不利益をもたらす可能性があるからだと思われます。

私の試したブラウザでは、firefoxで無効、その他のブラウザで有効でした。 firefoxで無効だったのはアドオンのせいかも知れませんが、詳細は未確認です。

・結合子 / 位置関係で要素をたどる

単体セレクタシーケンス1で得られた要素リストのそれぞれの要素の子孫や兄弟を探します。 見つかった要素で新たな要素リストを作り単体セレクタシーケンス2に渡します。

  • 子孫結合子 : シーケンス1 シーケンス2 ... 空白文字で区切る。入力要素の子孫要素。
  • 子結合子 : シーケンス1 > シーケンス2 ... 入力要素の直接の子要素。
  • 隣接兄弟結合子 : シーケンス1 + シーケンス2 ... 兄弟要素の中で、入力要素の直後。
  • 一般兄弟結合子 : シーケンス1 ~ シーケンス2 ... 兄弟要素の中で、入力要素以降。(入力要素自体は含まない)

ちょっと省略して書きましたが、入力要素というのは「シーケンス1で得られた要素リストのそれぞれの要素」です。 この説明のために適当に作った単語です。 兄弟結合子という呼称について、仕様書の日本語訳から単語を引っ張ってきました。 「兄弟結合子って、後ろの要素しか使わないなら弟結合子じゃないの?」と思ったけど、まぁそのままで。

実際にシーケンス2に渡される要素リストがどのようなものかは全称セレクタ*で確認できます。 サンプル1に次のようなスタイルを追加して「 >結合子 」の様子を見てみましょう。

.entry[id $= "1"] {
    background-color:green;
}
.entry[id $= "1"] > * {
    border:solid 3px black;
}
.entry[id $= "1"] > .content {
    text-decoration:line-through;
}

・サンプル5

サンプル5のキャプチャ

背景が緑色のdiv要素がシーケンス1( .entry[id $= "1"] )の絞り込んだやつです。 #l1、#c1、#r1の3つの要素がリストアップされています。

次に .entry[id $= "1"] > * で「 >結合子 」がリストアップした要素が分かります。 ボーダーの太い、#l1、#c1、#r1の直接の子要素9個がそれです。

最後のセレクタ、 .entry[id $= "1"] > .content を見るとシーケンス2が「.content」の絞り込みをしているのが分かります。 最終リストに挙げられた要素のテキストには取り消し線がかかっています。

とりあえず「 >結合子 」の挙動を見てみました。 他の結合子も「どの位置関係で要素を探してくるか?」という違いはあるものの、挙動の大枠は一緒です。 それぞれの結合子が実際にどう動くかは次のサンプルで確認してください。

このようなスタイルをサンプル1に追加したサンプル6を示します。

#left div {
    border:solid 3px black;
}
#right > div {
    border:dashed 3px black;
}
#c1 + div {
    background-color:green;
}
#c1 ~ div {
    text-decoration:line-through;
}

・サンプル6

サンプル6のキャプチャ

・その他のメモ

結合子*の勘違い

たまに結合子の説明で次のようなものが載っていることがあります。

  • シーケンス1 * シーケンス2 ... 入力要素の孫以降の子孫要素。

確かに仕様書を見ればそれっぽい記述はありますが、そこに書いてある*は結合子ではありません。 全称セレクタです。 「子孫結合子 → 全称セレクタ → 子孫結合子の順に書けば1世代空けることができる」という例であり、*を結合子として紹介している訳ではありません。 誤訳を元にした説明文なので注意しましょう。

css4で提案されているセレクタの対象決定

css4で提案されてはいるけど実際に実装しているブラウザが世の中にあるかどうかすら分からない項目です。 適当に訳すと、名前は「セレクタの対象決定」でいいのかな? 個人的に将来普及して欲しいと思っているのでピックアップしておきます。

単体セレクタシーケンス(css4の記述ではcompound selector)と結合子がいくつか連なっているセレクタで使います。 最後以外の単体セレクタシーケンスの前(後?)にエクスクラメーションマーク!を付けると、全体のマッチングが終わった後に結果からそのシーケンスまでさかのぼって、その単体セレクタシーケンスの対象が最終結果として採用されます。 これにより、特定の子孫を持つ要素や兄弟を持つ要素などを探すことができるようになります。

div.entry[short] !table.category td.remark

などとするとremarkクラスを持ったtd要素をまず特定し、そこから途中経過をさかのぼりtableを最終結果とします。

ドキュメントの書き手にとっては文書構造がしっかりしていれば全く必要のないものですが、ユーザスタイルシートを書くには大変便利ですよね。 ぜひ普及してほしいものです。

しかし、仕様書のWorking Draftを見てもSelectors Overviewでは!が後ろに付いていたりDetermining the Subject of a Selectorの例では前に付いていたりと仕様が固まっていない雰囲気です。 記号も!の前は$や?だったりと変遷しています。 主要ブラウザで実装されるにはもうちょっとかかるかもしれませんね。