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

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, aber ich kann es trotzdem verwenden und gleicht Knoten ab. Im zweiten Beispiel ist body kein Nachfolger 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

In WebKit wird die Pseudoklasse :scope in querySelector[All]() seit Kurzem unterstützt. 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 einzugrenzen. Genau, 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 weniger Fläche der Browser für die Auswahlabgleiche hat, desto schneller sind die Suchanfragen.

In meinem Experiment dauert WebKit etwa 1, 5–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.