Pseudo-Elemente mit querySelector

Click me, Touch me

Pseudo-Elemente wie ::before und ::after ersparen zusätzliche HTML-Elemente, aber früher galt, dass Javascript nicht auf Pseudo-Elemente wie ::before und ::after zugreifen kann, weil sie nicht im DOM liegen. Javascript querySelector und getComputedStyle bilden zusammen eine Lösung.

querySelector schafft dieses kleine Kunststück aber doch – mit etwas Hilfe von getComputedStyle. getComputedStyle und querySelector verhelfen Javascript aber nur einen lesenden Zugriff auf Pseudo-Selektoren.

var boxShadow = window.getComputedStyle (
    document.querySelector('.circle'), ':before'
).getPropertyValue('box-shadow');
console.log (boxShadow)

window.getComputedStyle () gibt alle CSS-Eigenschaften eines Elements zurück, und zwar nachdem Eigenschaften aus CSS-Dateien, aus dem style-Tag und inline-CSS angwendet wurden.

var properties = window.getComputedStyle (elem, status);

Dabei ist elem der Name des Elements und status der Name eines Pseudoelements wie : before, : after, : first-line oder null für das Objekt selbst

Quelle: Get Pseudo-Element Properties with JavaScript

CSS für Pseudo-Elemente mit Javascript ändern

Javascript kann Stile für Pseudo-Elemente nicht auf dem klassischen Weg per style-Attribut einsetzen und es gibt zwar getComputedStyle, aber kein setComputedStyle.

Auf zwei Umwegen kommt Javascript allerdings doch zum Zug:

  • Durch Einsetzen einer CSS Klasse, die dem Pseudo-Element die benötigten Stile beibringt.
  • Durch dynamisches Einsetzen von CSS-Regeln in ein style-Element im Head der Seite, in das per innerHTML die Stile für das Pseudo-Element gesetzt werden.

CSS class für Pseudo-Selektoren

Das ist der einfachste Weg, um Pseudo-Elemente per Javascript dynamisch zu ändern. Hier ist es ein einfaches Menü, das sich à la Accordion auf- und zuklappen lässt und den aktuellen Zustand durch das Dreieck vor der Überschrift anzeigt.

Label 1
  • Punkt 1
  • Punkt 2
  • Punkt 3
Label 2
  • Punkt 4
  • Punkt 5
  • Punkt 6
Label 3
  • Punkt 7
  • Punkt 8
  • Punkt 9

Ohne zusätzliche HTML-Elemente bleibt die Struktur des Accordion einfach und überschaubar.

HTML
<div class="accordion">

   <div class="label">Label 1</div>
   <ul class="panel">
      <li>Punkt 1</li>
      <li>Punkt 2</li>
      <li>Punkt 3</li>
   </ul>

   <div class="label">Label 2</div>
   <ul class="panel">
      <li>Punkt 4</li>
      <li>Punkt 5</li>
      <li>Punkt 6</li>
   </ul>
   …
</div>

Das Dreieck vor dem jeweiligen Label entsteht durch ein Pseudo-Element. Wenn ein Label geklickt wird, wird ein nach unten gerichtetes Dreieck eingesetzt.

CSS
.accordion .label { 
   margin: 0; padding: 4px; 
   background: hsl(200,50%,70%); margin-bottom: 2px;
}

.accordion .label::before {
   content: "▶ ";
}
.accordion .label.open::before {
   content: "▼ ";
}
.accordion ul { 
   height: 0; max-height: 0; 
   overflow: hidden; transition: all 1s;
}
.accordion ul.open {
   height: auto; max-height: 1000px;
}

Wenn das Script ein Klick-Event abfängt, muss zuerst ein eventuell schon geöffnetes Panel geschlossen werden: Die CSS-Klasse open aus allen Elementen entfernen.

Javascript
<script>
var label = document.querySelectorAll(".label");
for (var i=0; i<label.length; i++) {
   label[i].onclick = function () {
      var open = document.querySelectorAll(".open");
      if (open.length > 0) {
         for (j=0; j<open.length; j++) {
            open[j].classList.remove("open");
         }
      }
      this.nextElementSibling.classList.add("open");
      this.classList.add("open");
   }
}
</script>

style-Element erzeugen und einfügen

Die Alternative ist das Erzeugen und Einfügen eines neuen <style>-Tags im head der Seite.

var label = document.querySelectorAll(".label");
for (var i=0; i<label.length; i++) {
   label[i].onclick = function () {
      if (document.getElementById("new")) {
         var elem = document.getElementById("new");
         elem.remove();
      }
      var open = document.querySelectorAll(".open");
      if (open.length > 0) {
         for (j=0; j<open.length; j++) {
            open[j].classList.remove("open");
         }
      }
      var style = document.createElement('style'); 
      style.id = "new";
     
      style.innerHTML = '#nav_menu h5.open::before { content: "▼ "}' + 
                        '#nav_menu ul.open { height: auto; max-height: 1000px}';
      document.querySelector('head').appendChild(style);
      this.nextElementSibling.classList.add('open');
      this.classList.add("open");
   }
}

Das ist einerseits länger und wirkt schon von vornherein komplizierter. Das Einfügen eines neuen <style>-Tags macht Sinn, nicht zwei oder drei Elementen eine CSS-Klasse oder ein style-Attribut hinzugefügt wird, sondern sehr viele Elemente mit neuen Attributen ausgerüstet werden müssten. Dann ist ein dynamisch eingesetztes globales Stylesheet die schnellere Variante, die das Markup der Seite nicht ändert.

Mehr zu CSS Pseudo-Elementen