Для чего нужен псевдокласс CSS :scope?

Эрик Бидельман

:scope определяется в CSS Selectors 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>

Это окрашивает элементы li в первом ul в красный цвет и, согласно правилу :scope , вокруг ul помещается граница. Это потому, что в контексте этого <style scoped> ul соответствует :scope . Это местный контекст. Если бы мы добавили правило :scope во внешний <style> , оно соответствовало бы всему документу. По сути, эквивалент :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 , а затем отфильтровывает те, которые не входят в элемент scope . Это работает, но может привести к странному поведению, если вы не будете осторожны. Читай дальше.

Когда querySelector идет не так

В спецификации селекторов есть очень важный момент , который люди часто упускают из виду. Даже когда querySelector[All]() вызывается для элемента, селекторы по-прежнему оцениваются в контексте всего документа . Это означает, что могут произойти непредвиденные вещи:

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

Что за черт! В первом примере моим элементом является ul , но я все еще могу его использовать и сопоставлять узлы. Во втором случае body даже не является потомком моего элемента, но " body ul a " по-прежнему соответствует. И то, и другое сбивает с толку и не соответствует ожиданиям.

Здесь стоит провести сравнение с jQuery, который использует правильный подход и делает то, что вы ожидаете:

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

…введите :scope чтобы решить эти семантические махинации.

Исправление querySelector с помощью :scope

Недавно в WebKit появилась поддержка использования псевдокласса :scope в querySelector[All]() . Вы можете протестировать это в Chrome Canary 27.

Вы можете использовать его , ограничивая селекторы элементом контекста . Давайте посмотрим пример. Далее :scope используется для «охвата» селектора поддеревом элемента области. Правильно, я три раза сказал масштаб!

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

Использование :scope делает семантику методов querySelector() немного более предсказуемой и соответствующей тому, что уже делают другие, такие как jQuery.

Победа в производительности?

Еще нет :(

Мне было любопытно, дает ли использование :scope в qS/qSA повышение производительности. Итак… как хороший инженер, я организовал тест . Мое обоснование: меньшая площадь поверхности браузера для сопоставления селекторов означает более быстрый поиск.

В моем эксперименте использование WebKit в настоящее время занимает примерно в 1,5–2 раза больше времени, чем использование :scope . Черт возьми! Когда crbug.com/222028 будет исправлен, его использование теоретически должно дать вам небольшой прирост производительности по сравнению с его отсутствием.