CSS :scope 疑似クラスの役割

:scopeCSS セレクタ 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 が修正されたら、理論的にはこれを使用すると、使用しない場合に比べてパフォーマンスがわずかに向上するはずです。