: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 будет исправлен, его использование теоретически должно дать вам небольшой прирост производительности по сравнению с его отсутствием.