:scope
est défini dans Sélecteurs CSS 4 comme suit:
Pseudo-classe représentant tous les éléments de l'ensemble d'éléments de référence contextuelle. Il s'agit d'un ensemble d'éléments (potentiellement vide) spécifié explicitement, tel que celui spécifié par
querySelector()
ou l'élément parent d'un élément<style scoped>
, qui permet de définir le champ d'application d'un sélecteur afin qu'il ne corresponde qu'à un sous-arbre.
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'intégralité du document, vous pouvez limiter le jeu 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. Dans le deuxième exemple, le navigateur recherche tous les éléments a
, puis filtre ceux qui ne figurent 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
Il existe un point très important dans la spécification des sélecteurs que les utilisateurs négligent souvent. Même lorsque querySelector[All]()
est appelé sur un élément, les sélecteurs sont toujours évalués 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 exact, 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 outils comme jQuery.
Amélioration des performances ?
Pas encore :(
Je me demandais si l'utilisation de :scope
dans qS/qSA améliore les performances. Alors, en bon ingénieur, j'ai conçu un test. Ma logique: moins de surface pour que le navigateur puisse effectuer la mise en correspondance des sélecteurs signifie des recherches plus rapides.
Dans mon test, WebKit prend actuellement environ 1,5 à 2 fois plus de temps que si :scope
n'était pas utilisé. Mince ! Lorsque crbug.com/222028 sera corrigé, son utilisation devrait théoriquement vous offrir un léger regain de performances par rapport à l'utilisation de la version non optimisée.