A cosa serve la pseudo-classe CSS :scope?

: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, grazie alla regola :scope, viene applicato un bordo al ul. Questo perché nel contesto di questo <style scoped>, ul corrisponde a :scope. È il contesto locale. Se aggiungiamo una regola :scope all'interno di :scope, questa corrisponderà all'intero documento.<style> 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 riesco comunque a utilizzarlo e ad abbinare i 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 utilizzarlo per limitare i selettori a un elemento del 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.

Miglioramento delle prestazioni?

Non ancora :(

Mi chiedevo se l'utilizzo di :scope in qS/qSA migliorasse il rendimento. Così, da buon ingegnere, ho creato un test. Il mio ragionamento: 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. Accidenti! Quando il problema crbug.com/222028 verrà risolto, l'utilizzo dovrebbe teoricamente offrire un leggero aumento delle prestazioni rispetto a non utilizzarlo.