Wozu dient die CSS-„:scope“-Pseudoklasse?

:scope wird in CSS Selectors 4 so definiert:

Eine Pseudoklasse, die jedes Element im Satz der Elemente für die Kontextreferenz darstellt. Dies ist eine (potenziell leere) explizit angegebene Gruppe von Elementen, z. B. die vom querySelector() oder dem übergeordneten Element eines <style scoped>-Elements angegebene Gruppe, mit der ein Auswahlvorgang so eingegrenzt wird, dass er nur innerhalb eines untergeordneten Knotens übereinstimmt.

Ein Beispiel für die Verwendung in einer <style scoped> (weitere Informationen):

<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>

Dadurch werden die li-Elemente im ersten ul rot eingefärbt und aufgrund der :scope-Regel wird ein Rahmen um den ul eingefügt. Das liegt daran, dass im Kontext dieser <style scoped> die ul mit :scope übereinstimmt. Es ist der lokale Kontext. Wenn wir eine :scope-Regel in die äußere <style> einfügen, würde sie mit dem gesamten Dokument übereinstimmen. Im Wesentlichen entspricht dies :root.

Kontextelemente

Die Element-Version von querySelector() und querySelectorAll() ist Ihnen wahrscheinlich schon bekannt. Anstatt das gesamte Dokument abzufragen, können Sie den Ergebnissatz auf ein kontextbezogenes Element beschränken:

<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>

Wenn diese aufgerufen werden, gibt der Browser eine NodeList zurück, die so gefiltert ist, dass nur die Knoten enthalten sind, die a) mit der Auswahl übereinstimmen und b) auch Nachkommen des Kontextelements sind. Im zweiten Beispiel sucht der Browser also nach allen a-Elementen und filtert dann die heraus, die nicht im scope-Element enthalten sind. Das funktioniert, kann aber zu merkwürdigem Verhalten führen, wenn Sie nicht vorsichtig sind. Lesen Sie weiter.

Wenn querySelector fehlschlägt

In der Spezifikation für Auswahlen gibt es einen sehr wichtigen Punkt, der oft übersehen wird. Auch wenn querySelector[All]() auf ein Element angewendet wird, werden Selektoren immer im Kontext des gesamten Dokuments ausgewertet. Das kann unvorhergesehene Folgen haben:

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

Was soll das? Im ersten Beispiel ist ul mein Element, ich kann es aber trotzdem verwenden und es wird mit Knoten abgeglichen. Im zweiten Fall ist body nicht einmal ein Abkömmling meines Elements, aber „body ul a“ stimmt trotzdem überein. Beides ist verwirrend und nicht das, was Sie erwarten würden.

Hier lohnt sich ein Vergleich mit jQuery, das den richtigen Ansatz verfolgt und das tut, was Sie erwarten:

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

… geben Sie :scope ein, um diese semantischen Spielereien zu lösen.

querySelector mit :scope korrigieren

WebKit unterstützt seit Kurzem die Verwendung der Pseudoklasse :scope in querySelector[All](). Sie können es in Chrome Canary 27 testen.

Sie können damit Selektoren auf ein Kontextelement beschränken. Sehen wir uns ein Beispiel an. Im Folgenden wird :scope verwendet, um den Selector auf den untergeordneten Knoten des Elements mit dem Gültigkeitsbereich anzuwenden. Ja, ich habe „Umfang“ dreimal gesagt.

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

Durch die Verwendung von :scope wird die Semantik der querySelector()-Methoden etwas vorhersehbarer und entspricht dem, was andere wie jQuery bereits tun.

Leistungsgewinn?

Noch nicht :(

Ich wollte wissen, ob die Verwendung von :scope in qS/qSA die Leistung steigert. Also habe ich wie ein guter Entwickler einen Test zusammengestellt. Meine Begründung: Je kleiner die Fläche ist, auf der der Browser die Auswahl abgleichen muss, desto schneller sind die Suchanfragen.

In meinem Test dauert es mit WebKit derzeit etwa 1, 5- bis 2-mal länger als ohne :scope. Mist! Wenn crbug.com/222028 behoben ist, sollte die Verwendung der Funktion theoretisch zu einer leichten Leistungssteigerung führen.