:scope
se define en los selectores CSS 4 de la siguiente manera:
Es una seudoclase que representa cualquier elemento que se encuentra en el conjunto de elementos de referencia contextual. Este es un conjunto de elementos especificado de forma explícita (potencialmente vacío), como el especificado por
querySelector()
, o el elemento superior de un elemento<style scoped>
, que se usa para "limitar" un selector de modo que solo coincida con un subárbol.
Un ejemplo de uso de esto es dentro de una <style scoped>
(más información):
<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>
De esta manera, los elementos li
del primer ul
se pintan de rojo y, debido a la regla :scope
, se coloca un borde alrededor de ul
. Esto se debe a que, en el contexto de esta <style scoped>
, la ul
coincide con :scope
. Es el contexto local. Si agregáramos una regla :scope
en el <style>
externo, coincidiría con todo el documento. En esencia, equivale a :root
.
Elementos contextuales
Es probable que conozcas la versión Element
de querySelector()
y querySelectorAll()
. En lugar de consultar todo el documento, puedes restringir el conjunto de resultados a un 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>
Cuando se los llama, el navegador muestra un NodeList
que se filtra para incluir solo el conjunto de nodos que a.) coinciden con el selector y b.) que también son descendientes del elemento de contexto. En el segundo ejemplo, el navegador encuentra todos los elementos a
y, luego, filtra los que no están en el elemento scope
. Esto funciona, pero puede generar un comportamiento extraño si no tienes cuidado. Más información.
Cuando querySelector falla
Hay un muy punto importante en la especificación de los selectores que las personas suelen pasar por alto. Incluso cuando se invoca querySelector[All]()
en un elemento, los selectores se evalúan en el contexto de todo el documento. Esto significa que pueden ocurrir cosas imprevistas:
scope.querySelectorAll('ul a').length); // 1
scope.querySelectorAll('body ul a').length); // 1
¿Qué rayos está pasando? En el primer ejemplo, ul
es mi elemento, pero aún puedo usarlo y coincide con los nodos. En la segunda, body
ni siquiera es un descendiente de mi elemento, pero "body ul a
" igual coincide. Ambas opciones son confusas y no son lo que esperas.
Vale la pena hacer una comparación con jQuery aquí, que adopta el enfoque correcto y hace lo que esperas:
$(scope).find('ul a').length // 0
$(scope).find('body ul a').length // 0
Ingresa :scope
para resolver estas travesuras semánticas.
Cómo corregir querySelector con :scope
WebKit recibió compatibilidad con el uso de la seudoclase :scope
en querySelector[All]()
. Puedes probarlo en Chrome Canary 27.
Puedes usarlo para restringir selectores a un elemento de contexto. Veamos un ejemplo. A continuación, :scope
se usa para "limitar" el selector con respecto al subárbol del elemento de alcance. Así es. Dije de alcance tres veces.
scope.querySelectorAll(':scope ul a').length); // 0
scope.querySelectorAll(':scope body ul a').length); // 0
scope.querySelectorAll(':scope a').length); // 1
Usar :scope
hace que la semántica de los métodos querySelector()
sea un poco más predecible y se alinea con lo que otros como jQuery ya hacen.
¿Ganaste en el rendimiento?
Aún no :(
Tenía curiosidad por saber si usar :scope
en qS/qSA aumentaba el rendimiento. Entonces... como un buen ingeniero, hice una prueba. La razón:
En mi experimento, WebKit tarda entre 1.5 y 2 veces más tiempo que si no usara :scope
. ¡Datos! Cuando se corrige crbug.com/222028, en teoría, su uso debería aumentar ligeramente el rendimiento en comparación con no usarlo.