:scope
è definito in CSS Selectors 4 come:
Una pseudo-classe che rappresenta qualsiasi elemento nell'insieme di elementi di riferimento contestuale. Si tratta di un insieme (potenzialmente vuoto) di elementi specificati esplicitamente, come quello specificato da
querySelector()
o dall'elemento principale di un elemento<style scoped>
, che viene utilizzato per "definire l'ambito" di un selettore in modo che corrisponda solo all'interno di un sottoalbero.
Un esempio di utilizzo è all'interno di un <style scoped>
(scopri di più):
<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>
In questo modo, gli elementi li
nel primo ul
vengono colorati di rosso e, a causa della regola :scope
, viene applicato un bordo intorno al ul
. Questo perché nel contesto di questo <style scoped>
, il valore di ul
corrisponde a :scope
. È il contesto locale. Se aggiungessimo una regola :scope
nel campo <style>
esterno, corrisponderà all'intero documento. Essenzialmente, è equivalente a :root
.
Elementi contestuali
Probabilmente conosci la versione Element
di querySelector()
e querySelectorAll()
. Anziché eseguire query sull'intero documento, puoi limitare il set di risultati a un elemento contestuale:
<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 vengono chiamati, il browser restituisce un NodeList
filtrato in modo da includere solo l'insieme di nodi che a.) corrispondono al selettore e b.) sono anche discendenti dell'elemento di contesto. Pertanto, nel secondo esempio, il browser trova tutti gli elementi a
, quindi filtra quelli non presenti nell'elemento scope
. Funziona, ma se non fai attenzione può portare a comportamenti bizzarri. Continua a leggere.
Quando querySelector non va a buon fine
Nella specifica dei selettori c'è un punto molto importante che spesso viene trascurato. Anche quando querySelector[All]()
viene invocato su un elemento, i selettori vengono comunque valutati nel contesto dell'intero documento. Ciò significa che possono verificarsi eventi imprevisti:
scope.querySelectorAll('ul a').length); // 1
scope.querySelectorAll('body ul a').length); // 1
WTF! Nel primo esempio, ul
è il mio elemento, ma posso ancora utilizzarlo e corrisponde ai nodi. Nel secondo, body
non è nemmeno un discendente del mio elemento, ma "body ul a
" corrisponde comunque. Entrambi sono confusi e non sono ciò che ti aspetti.
Vale la pena fare un confronto con jQuery, che adotta l'approccio giusto e fa ciò che ti aspetti:
$(scope).find('ul a').length // 0
$(scope).find('body ul a').length // 0
…inserisci :scope
per risolvere questi problemi semantici.
Correzione di querySelector con :scope
WebKit ha recentemente implementato il supporto per l'utilizzo dell'pseudo-classe :scope
in querySelector[All]()
. Puoi testarlo in Chrome Canary 27.
Puoi utilizzare questa opzione per limitare i selettori a un elemento di contesto. Vediamo un esempio. Di seguito, :scope
viene utilizzato per "limitare" il selettore al sottoalbero dell'elemento ambito. Esatto, ho detto ambito tre volte.
scope.querySelectorAll(':scope ul a').length); // 0
scope.querySelectorAll(':scope body ul a').length); // 0
scope.querySelectorAll(':scope a').length); // 1
L'utilizzo di :scope
rende la semantica dei metodi querySelector()
un po' più prevedibile e in linea con ciò che fanno già altri, come jQuery.
Conquista le prestazioni?
Non ancora :(
Mi chiedevo se l'utilizzo di :scope
in qS/qSA migliorasse le prestazioni. Così, da buon ingegnere, ho creato un test. La mia motivazione: meno superficie per la corrispondenza dei selettori del browser significa ricerche più rapide.
Nel mio esperimento, WebKit attualmente impiega circa 1,5-2 volte più tempo rispetto all'utilizzo di :scope
. Drat! Una volta risolto il problema con crbug.com/222028, il suo utilizzo dovrebbe in teoria incrementare le prestazioni rispetto al suo utilizzo.