:scope
est défini dans Sélecteurs CSS 4 comme suit :
Pseudo-classe qui représente tout élément figurant dans l'ensemble d'éléments de référence contextuel. Il s'agit d'un ensemble d'éléments (potentiellement vide) explicitement spécifié, comme celui spécifié par
querySelector()
, ou de l'élément parent d'un élément<style scoped>
, qui est utilisé pour "porter" un sélecteur afin qu'il ne corresponde qu'à une sous-arborescence.
Voici un exemple d'utilisation dans un fichier <style scoped>
(en savoir plus) :
<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>
Les éléments li
du premier ul
sont alors colorés en rouge et, en raison de la règle :scope
, une bordure est appliquée autour du ul
. En effet, dans le contexte de cette <style scoped>
, ul
correspond à :scope
. Il s'agit du contexte local. Si nous ajoutions une règle :scope
dans le <style>
externe, elle correspondrait à l'intégralité du document. Équivaut essentiellement à :root
.
Éléments contextuels
Vous connaissez probablement la version Element
de querySelector()
et querySelectorAll()
. Au lieu d'interroger l'ensemble du document, vous pouvez limiter l'ensemble de résultats à un élément contextuel:
<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>
Lorsqu'ils sont appelés, le navigateur renvoie un NodeList
filtré pour n'inclure que l'ensemble des nœuds qui a.) correspondent au sélecteur et b.) sont également descendants de l'élément de contexte. Ainsi, dans le deuxième exemple, le navigateur trouve tous les éléments a
, puis filtre ceux qui ne se trouvent pas dans l'élément scope
. Cela fonctionne, mais cela peut entraîner un comportement bizarre si vous n'y faites pas attention. Lisez la suite.
Lorsque querySelector échoue
La spécification des sélecteurs comporte un aspect très important que les utilisateurs oublient souvent. Même lorsque la méthode querySelector[All]()
est appelée sur un élément, les sélecteurs continuent de procéder à l'évaluation dans le contexte de l'ensemble du document. Cela signifie que des événements inattendus peuvent se produire:
scope.querySelectorAll('ul a').length); // 1
scope.querySelectorAll('body ul a').length); // 1
Sérieusement ?! Dans le premier exemple, ul
est mon élément, mais je peux toujours l'utiliser et il correspond aux nœuds. Dans le second, body
n'est même pas un descendant de mon élément, mais "body ul a
" correspond toujours. Ces deux comportements sont déroutants et ne correspondent pas à ce que vous attendez.
Il est intéressant de comparer jQuery ici, qui adopte la bonne approche et fait ce que vous attendez :
$(scope).find('ul a').length // 0
$(scope).find('body ul a').length // 0
…entrez :scope
pour résoudre ces manigances sémantiques.
Correction de querySelector avec :scope
WebKit a récemment lancé la prise en charge de l'utilisation de la pseudo-classe :scope
dans querySelector[All]()
. Vous pouvez le tester dans Chrome Canary 27.
Vous pouvez l'utiliser pour restreindre les sélecteurs à un élément de contexte. Prenons un exemple. Dans l'exemple suivant, :scope
est utilisé pour "définir la portée" du sélecteur sur le sous-arbre de l'élément de portée. C'est bien cela, j'ai dit "champ d'application" trois fois.
scope.querySelectorAll(':scope ul a').length); // 0
scope.querySelectorAll(':scope body ul a').length); // 0
scope.querySelectorAll(':scope a').length); // 1
L'utilisation de :scope
rend la sémantique des méthodes querySelector()
un peu plus prévisible et conforme à ce que font déjà d'autres méthodes comme jQuery.
Performances gagnées ?
Pas encore :(
Je me demandais si l'utilisation de :scope
dans qS/qSA améliore les performances. Alors, en bon ingénieur, j'ai mis en place un test. J'entends par là: la taille réduite du navigateur pour la mise en correspondance des sélecteurs signifie que les recherches sont plus rapides.
Dans mon test, WebKit prend actuellement environ 1,5 à 2 fois plus de temps que sans :scope
. Mince ! Lorsque crbug.com/222028 est corrigé, son utilisation devrait théoriquement améliorer légèrement les performances plutôt que de ne pas l'utiliser.