:scope
é definido nos seletores de CSS 4 como:
Uma pseudoclasse que representa qualquer elemento que esteja no conjunto de elementos de referência contextual. Esse é um conjunto (possivelmente vazio) de elementos especificados explicitamente, como o especificado por
querySelector()
, ou o elemento pai de um elemento<style scoped>
, usado para "definir o escopo" de um seletor para que ele corresponda apenas a uma subárvore.
Um exemplo de uso disso está em um <style scoped>
(mais informações):
<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>
Isso colore os elementos li
no primeiro ul
vermelho e, devido à regra :scope
, coloca uma borda em torno do ul
. Isso ocorre porque, no contexto dessa <style scoped>
, a ul
corresponde à :scope
. É o contexto local. Se adicionássemos uma regra :scope
no <style>
externo, ela corresponderia a todo o documento. Basicamente, equivalente a :root
.
Elementos contextuais
Você provavelmente já conhece a versão Element
do querySelector()
e do querySelectorAll()
. Em vez de consultar o documento inteiro, você pode restringir o conjunto de resultados a um elemento contextual:
<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>
Quando eles são chamados, o navegador retorna um NodeList
filtrado para incluir apenas o conjunto de nós que a.) correspondem ao seletor e b.) que também são descendentes do elemento de contexto. Portanto, no segundo exemplo, o navegador encontra todos os elementos a
e, em seguida, filtra aqueles que não estão no elemento scope
. Isso funciona, mas pode causar um comportamento bizarro se você não tomar cuidado. Continue lendo.
Quando o querySeletor dá errado
Há um ponto muito importante na especificação dos seletores que as pessoas geralmente ignoram. Mesmo quando querySelector[All]()
é invocado em um elemento, os seletores ainda avaliam no contexto de todo o documento. Isso significa que imprevistos podem acontecer:
scope.querySelectorAll('ul a').length); // 1
scope.querySelectorAll('body ul a').length); // 1
É alguma piada? No primeiro exemplo, ul
é meu elemento, mas ainda consigo usá-lo e corresponde aos nós. Na segunda, body
não é nem um descendente do meu elemento, mas "body ul a
" ainda corresponde. As duas coisas são confusas e não são o que você esperaria.
Vale a pena fazer uma comparação com o jQuery aqui, que usa a abordagem certa e faz o que você espera:
$(scope).find('ul a').length // 0
$(scope).find('body ul a').length // 0
...insira :scope
para resolver esses truques semânticos.
Correção de querySeletor com :scope
O WebKit lançou recentemente compatibilidade com o uso da pseudoclasse :scope
em querySelector[All]()
. É possível testá-la no Chrome Canary 27.
É possível usar essa opção para restringir seletores a um elemento de contexto. Vamos ver um exemplo. A seguir, :scope
é usado para definir o escopo do seletor como a subárvore do elemento do escopo. Isso mesmo, eu disse o escopo três vezes!
scope.querySelectorAll(':scope ul a').length); // 0
scope.querySelectorAll(':scope body ul a').length); // 0
scope.querySelectorAll(':scope a').length); // 1
O uso de :scope
torna a semântica dos métodos querySelector()
um pouco mais previsível e alinhada com o que outros, como o jQuery, já estão fazendo.
Melhor desempenho?
Ainda não :(
Gostaria de saber se o uso do :scope
no qS/qSA melhora o desempenho. Então, como um bom engenheiro, fiz um teste. Minha lógica: menos área de superfície para o navegador fazer a correspondência de seletor significa pesquisas mais rápidas.
Em meu experimento, o WebKit leva atualmente de 1,5 a 2 vezes mais tempo do que não usar :scope
. Droga! Quando o site crbug.com/222028 for corrigido, teoricamente, o uso dele deve resultar em um pequeno aumento no desempenho do que quando o usuário não o faz.