Pseudo-Elemente mit querySelector

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.

Pseudo-Element mit Javascript

getComputedStyle und getPropertyValue

querySelector schafft dieses kleine Kunststück aber doch – mit etwas Hilfe von getComputedStyle und getPropertyValue. Diese wilde Konstruktion verhilft Javascript aber nur einen lesenden Zugriff CSS-Eigenschaften von Pseudo-Elementen.

.item::before {
    content: url(beachball.png);
    transform: scale(0.5);
    transform-origin: 0 0;
    box-shadow: 10px 20px 15px silver;
}

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.

const str = window.getComputedStyle(item, '::before') 
           .getPropertyValue('content');

Dabei ist item der Name des Elements. Der zweite Parameter ist der Name eines Pseudo-Elements wie ::before, ::after oder null für das Objekt selbst.

getPropertyValue() beschränkt die Ausgabe auf den Wert einer speziellen Eigenschaft – wie ‚content‘ oder ‚box-shadow‘ im Beispiel. Aber was ist mit color? color ist nicht gesetzt und begnügt sich mit der Voreinstellung rgb(0,0,0).

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>
let label = document.querySelectorAll(".label");
for (let i=0; i<label.length; i++) {
   label[i].onclick = function () {
      let open = document.querySelectorAll(".open");
      if (open.length > 0) {
         for (let 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.

let label = document.querySelectorAll(".label");
for (let i=0; i<label.length; i++) {
   label[i].onclick = function () {
      if (document.getElementById("new")) {
         let elem = document.getElementById("new");
         elem.remove();
      }
      let open = document.querySelectorAll(".open");
      if (open.length > 0) {
         for (let j=0; j<open.length; j++) {
            open[j].classList.remove("open");
         }
      }
      let 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.

2024-02-12 SITEMAP BLOG