CSS :scope 疑似クラスの役割

:scope は、CSS セレクタ 4 で次のように定義されています。

コンテキスト参照要素セット内の要素を表す疑似クラス。これは、querySelector()<style scoped> 要素の親要素など、明示的に指定された要素のセットです(空になることもあります)。これは、サブツリー内でのみ一致するようにセレクタの「スコープ」に使用されます。

<style scoped> 内での使用例を次に示します(詳細)。

<style>
    li {
    color: blue;
    }
</style>

<ul>
    <style scoped>
    li {
        color: red;
    }
    :scope {
        border: 1px solid red;
    }
    </style>
    <li>abc</li>
    <li>def</li>
    <li>efg</li>
</ul>

<ul>
    <li>hij</li>
    <li>klm</li>
    <li>nop</li>
</ul>

これにより、最初の ulli 要素が赤色になり、:scope ルールにより ul の周囲に枠線が付きます。これは、この <style scoped> のコンテキストでは、ul:scope と一致するためです。ローカル コンテキストです。外側の <style>:scope ルールを追加すると、ドキュメント全体と一致します。基本的には :root と同じです。

コンテキスト要素

querySelector()querySelectorAll()Element バージョンをご存じかもしれません。ドキュメント全体をクエリするのではなく、結果セットをコンテキスト要素に制限できます。

<ul>
    <li id="scope"><a>abc</a></li>
    <li>def</li>
    <li><a>efg</a></li>
</ul>
<script>
    document.querySelectorAll('ul a').length; // 2

    var scope = document.querySelector('#scope');
    scope.querySelectorAll('a').length; // 1
</script>

これらが呼び出されると、ブラウザは NodeList を返し、a.) セレクタに一致するノードと、b.) ノードのセットのみが含まれるようにフィルタされます。そのため、2 番目の例では、ブラウザはすべての a 要素を検出し、scope 要素内にない要素を除外しています。これは問題ありませんが、慎重に行わないと奇妙な動作につながる可能性があります。しっかりと確認しておきましょう。

querySelector が失敗した場合

セレクタの仕様には、見落とされがちな非常に 重要なポイントがあります。要素で querySelector[All]() が呼び出されても、セレクタはドキュメント全体のコンテキストで評価します。つまり、予期せぬ事態が起こり得ます。

    scope.querySelectorAll('ul a').length); // 1
    scope.querySelectorAll('body ul a').length); // 1

なんと!最初の例では、ul が要素ですが、引き続きこれを使用してノードを照合できます。2 つ目の要素では、body は要素の子孫でもありませんが、「body ul a」は引き続き一致します。どちらも混乱を招き、期待するものではありません。

ここでは jQuery と比較してみる価値があります。jQuery は適切なアプローチを採用し、期待どおりの動作を行います。

    $(scope).find('ul a').length // 0
    $(scope).find('body ul a').length // 0

...:scope を入力すると、これらのセマンティックなセマンティックを解決できます。

:scope による querySelector の修正

WebKit は、querySelector[All]():scope 疑似クラスを使用するためのサポートを最近リリースしました。Chrome Canary 27 でテストできます。

これを使用すると、セレクタをコンテキスト要素に制限できます。例を見てみましょう。以下では、:scope を使用してセレクタの「スコープ」をスコープ要素のサブツリーに設定しています。そうだ、スコープを 3 回言ったよ!

    scope.querySelectorAll(':scope ul a').length); // 0
    scope.querySelectorAll(':scope body ul a').length); // 0
    scope.querySelectorAll(':scope a').length); // 1

:scope を使用すると、querySelector() メソッドのセマンティクスがいくぶん予測しやすくなり、jQuery などの他のメソッドですでに行われていることと整合します。

パフォーマンスの向上

いいえ

qS/qSA で :scope を使用するとパフォーマンスが向上するかどうか知りたかったです。優秀なエンジニアのように、私はテストを投げかけました。私の根拠: ブラウザがセレクタのマッチングを行う表面積が小さくなると、検索が高速化されます。

私のテストでは、WebKit は現在、:scope を使用していない場合よりも約 1.5 ~ 2 倍長くかかっています。ドラァグ!crbug.com/222028 を修正すれば、使用しない場合と比べてパフォーマンスがわずかに向上するはずです。