A cosa serve la pseudo-classe CSS :scope?

Nei selettori CSS 4, :scope viene definito come:

Una pseudo-classe che rappresenta qualsiasi elemento presente nell'insieme di elementi di riferimento contestuale. Si tratta di un insieme di elementi (potenzialmente vuoto) specificati esplicitamente, come quello specificato da querySelector() o l'elemento principale di un elemento <style scoped>, che viene utilizzato per "ambito" un selettore in modo che corrisponda solo all'interno di un sottoalbero.

Un esempio di utilizzo di questa funzionalità è all'interno di un elemento <style scoped> (ulteriori informazioni):

<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 inserito un bordo intorno a ul. Questo perché nel contesto di questo <style scoped>, ul corrisponde a :scope. ma il contesto locale. Se aggiungessimo una regola :scope nella sezione <style> esterna, verrebbe applicata all'intero documento. In pratica, equivalente a :root.

Elementi contestuali

Probabilmente conosci la versione Element di querySelector() e querySelectorAll(). Anziché eseguire query sull'intero documento, puoi limitare l'insieme 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 richiamate, il browser restituisce un valore NodeList filtrato in modo da includere solo il set di nodi a.) che corrispondono al selettore e b.) anch'essi discendenti dell'elemento di contesto. Quindi nel secondo esempio, il browser trova tutti gli elementi a e filtra quelli non presenti nell'elemento scope. Questa regola funziona, ma può portare a comportamenti bizzarri se non fai attenzione. Continua a leggere.

Quando querySelector non funziona

C'è un punto davvero importante nelle specifiche dei selettori che spesso le persone trascurano. Anche quando querySelector[All]() viene richiamato su un elemento, i selettori continuano a valutare nel contesto dell'intero documento. Ciò significa che possono accadere cose inaspettate:

    scope.querySelectorAll('ul a').length); // 1
    scope.querySelectorAll('body ul a').length); // 1

Wow! 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 corrispondono alle aspettative.

In questo caso, vale la pena fare un confronto con jQuery, che adotta l'approccio giusto e fa ciò che ci si aspetta:

    $(scope).find('ul a').length // 0
    $(scope).find('body ul a').length // 0

... inserisci :scope per risolvere questi imbrogli semantici.

Correzione di querySelector con :scope

WebKit ha recentemente ottenuto il supporto per l'utilizzo della pseudo-classe :scope in querySelector[All](). Puoi testarlo in Chrome Canary 27.

Puoi utilizzarlo per limitare i selettori a un elemento di contesto. Vediamo un esempio. Di seguito, viene utilizzato :scope per "applicare" il selettore al sottoalbero dell'elemento di ambito. Esatto, ho detto scope 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 stanno già facendo altri come jQuery.

Prestazioni efficaci?

Non ancora :(

Mi chiedevo se l'uso di :scope in qS/qSA migliora le prestazioni. Quindi... da bravo ingegnere ho condotto un test. La mia motivazione: una minore superficie di esplorazione da parte del browser per la corrispondenza del selettore significa ricerche più veloci.

Nel mio esperimento, al momento WebKit richiede circa 1,5-2 volte in più rispetto al mancato utilizzo di :scope. Dramma! Quando crbug.com/222028 viene risolto, il suo utilizzo dovrebbe teoricamente migliorare le prestazioni rispetto al suo mancato utilizzo.