Matthias Scharwies: SELF-Wiki: Was ist das DOM?

problematische Seite

Guten Morgen,

hier hatte ich es schon angekündigt: JavaScript und das DOM/Was ist das DOM?

Wir hatten an verschiedenen Stellen nahezu gleichlautende Erklärungen, die ich jetzt zu einem Anfänger-Tutorial zusammengefasst habe:

  1. kurze Einleitung
  2. Überschrift #DOM als Baumstruktur
    vorher verlinkte die Einleitung gleich zu XML/.../Baumstruktur
    Jetzt wird der Elementbaum gezeigt, der Begriff node eingeführt und mit dem „Node-Inspektor“ veranschaulicht.
  3. #Elementknoten
    muss da noch ein Beispiel hin, oder reicht der „Node-Inspektor“?
  4. Attributknoten
    Wer hat eine Idee zu einem besseren Beispiel, das evtl. auch mit Element.hasAttributes arbeitet.
  5. 2018 hatte ich schon gefragt, ob es einen besseren Begriff als DOM-Methoden gibt. Die beiden h3-Überschriften und Abschnitte könnten evtl. nach oben, evtl. aber auch nicht. Würde ich gerne diskutieren.

Die im Wiki vielfach vorkommende Liste der „DOM-Methoden“ würde ich ebenfalls gerne auf den Prüfstand stellen:

  • getElementById(): kann auf Elemente zugreifen, die ein dokumentweit eindeutiges id-Attribut enthalten
  • getElementsByName(): kann auf Elemente zugreifen, die einen Namen besitzen (er muss nicht unbedingt eindeutig sein)
  • getElementsByTagName(): kann auf alle Elemente zugreifen in der Form: "liefere mir das 27. td-Element im Dokument".
  • querySelector(): gibt das erste Element zurück, das dem angegebenen CSS-Selektor entspricht.
  • querySelectorAll(): gibt eine Liste von Elementen zurück, die dem angegebenen CSS-Selektor (auch mehrere, durch Komma getrennte, Angaben möglich) entsprechen

Imho würde ich nur die letzten beiden und classList empfehlen. Was meint ihr?

Herzliche Grüße

Matthias Scharwies

--
Was ist eine Signatur?
  1. problematische Seite

    @@Matthias Scharwies

    • getElementById(): kann auf Elemente zugreifen, die ein dokumentweit eindeutiges id-Attribut enthalten
    • getElementsByName(): kann auf Elemente zugreifen, die einen Namen besitzen (er muss nicht unbedingt eindeutig sein)
    • getElementsByTagName(): kann auf alle Elemente zugreifen in der Form: "liefere mir das 27. td-Element im Dokument".

    ?? getElementsByTagName() liefert alle Elemente dieses Namens. Für das 27. td-Element ist Weiteres erforderlich, was wohl nichts in dieser Übersicht verloren hat.

    • querySelector(): gibt das erste Element zurück, das dem angegebenen CSS-Selektor entspricht.
    • querySelectorAll(): gibt eine Liste von Elementen zurück, die dem angegebenen CSS-Selektor (auch mehrere, durch Komma getrennte, Angaben möglich) entsprechen

    Imho würde ich nur die letzten beiden und classList empfehlen. Was meint ihr?

    Ja, mit querySelector()/querySelectorAll() hat man ein einheitliches Interface für alle Fälle.

    getElementById() ist so weit verbreitet, dass es Erwähnung verdient, wobei gesagt werden muss, dass getElementById('a') dasselbe tut wie querySelector('#a').

    Das trifft auf die anderen Methoden nicht zu; die könnte man unter Sonstiges verbuchen. Einfach sagen, dass es weitere Methoden gibt (getElementsByTagName() könnte man dabei namentlich erwähnen, muss man aber nicht), die man aber nicht mehr braucht, da man ja jetzt querySelector()/querySelectorAll() hat.

    Kwakoni Yiquan

    --
    Ad astra per aspera
    1. problematische Seite

      Moin Gunnar,

      getElementById() ist so weit verbreitet, dass es Erwähnung verdient, wobei gesagt werden muss, dass getElementById('a') dasselbe tut wie querySelector('#a').

      Das trifft auf die anderen Methoden nicht zu; die könnte man unter Sonstiges verbuchen. Einfach sagen, dass es weitere Methoden gibt (getElementsByTagName() könnte man dabei namentlich erwähnen, muss man aber nicht), die man aber nicht mehr braucht, da man ja jetzt querySelector()/querySelectorAll() hat.

      wie ist denn eigentlich die Performance im Vergleich zwischen den spezifischen Methoden und „query selector“?

      Meine Erwartung ist, dass das Parsen des „query strings“ etwas mehr Zeit und Rechenleistung benötigt als schon direkt im DOM den entsprechend Lookup durchzuführen.

      Viele Grüße
      Robert

    2. problematische Seite

      Servus!

      @@Matthias Scharwies

      • getElementById(): kann auf Elemente zugreifen, die ein dokumentweit eindeutiges id-Attribut enthalten
      • getElementsByName(): kann auf Elemente zugreifen, die einen Namen besitzen (er muss nicht unbedingt eindeutig sein)
      • getElementsByTagName(): kann auf alle Elemente zugreifen in der Form: "liefere mir das 27. td-Element im Dokument".

      ?? getElementsByTagName() liefert alle Elemente dieses Namens. Für das 27. td-Element ist Weiteres erforderlich, was wohl nichts in dieser Übersicht verloren hat.

      • querySelector(): gibt das erste Element zurück, das dem angegebenen CSS-Selektor entspricht.
      • querySelectorAll(): gibt eine Liste von Elementen zurück, die dem angegebenen CSS-Selektor (auch mehrere, durch Komma getrennte, Angaben möglich) entsprechen

      Imho würde ich nur die letzten beiden und classList empfehlen. Was meint ihr?

      Ja, mit querySelector()/querySelectorAll() hat man ein einheitliches Interface für alle Fälle.

      getElementById() ist so weit verbreitet, dass es Erwähnung verdient, wobei gesagt werden muss, dass getElementById('a') dasselbe tut wie querySelector('#a').

      Das trifft auf die anderen Methoden nicht zu; die könnte man unter Sonstiges verbuchen. Einfach sagen, dass es weitere Methoden gibt (getElementsByTagName() könnte man dabei namentlich erwähnen, muss man aber nicht), die man aber nicht mehr braucht, da man ja jetzt querySelector()/querySelectorAll() hat.

      Ja, danke! Seh' ich auch so!

      Herzliche Grüße

      Matthias Scharwies

      --
      Was ist eine Signatur?
    3. problematische Seite

      Hallo zusammen,

      Ja, mit querySelector()/querySelectorAll() hat man ein einheitliches Interface für alle Fälle.

      getElementById() ist so weit verbreitet, dass es Erwähnung verdient, wobei gesagt werden muss, dass getElementById('a') dasselbe tut wie querySelector('#a').

      Das trifft auf die anderen Methoden nicht zu; die könnte man unter Sonstiges verbuchen. Einfach sagen, dass es weitere Methoden gibt (getElementsByTagName() könnte man dabei namentlich erwähnen, muss man aber nicht), die man aber nicht mehr braucht, da man ja jetzt querySelector()/querySelectorAll() hat.

      Kann man für Neueinsteigende so machen, wobei die Erwähnung von getElementById(), getElementsByName(), getElementsByClassName(), getElementsByTagName() und (in speziellen Kontexten) getElementsByTagNameNS() durchaus nützlich sein kann, wenn man es etwa mit älterem Code zutun hat und Anpassungen durchführen möchte.

      Erwähnenswert ist aber, dass querySelector() / querySelectorAll() wie andere Elements-Methoden nicht nur auf dem Document- sondern auch auf dem Element-Interface wirken können:

      <ul id="spezielle_liste">
        <li>Text</li>
        <li>Text</li>
        <li>Text</li>
      </ul>
      <script>
        const ul = document.querySelector("#spezielle_liste");
        const ul_li = ul.querySelectorAll("li");
        console.log(ul_li.length); // 3
      </script>
      

      Interessanterweise klappt das mittlerweile sogar bei AJAXifizierten Inhalten, also eingelesenen XML-Strukturen. Dort war bisher xmlroot.getElementsByTagName("xmlelement") zielführend(er).

      Grüße,
      Thomas

    4. problematische Seite

      Hallo Gunnar,

      wobei gesagt werden muss, dass getElementById('a') dasselbe tut wie querySelector('#a').

      Auf Ebene von Document oder DocumentFragment: Ja.
      Auf Ebene von Element: Nein.

      Denn getElementById ist eine Methode von NonElementParentNode, ein Mixin, das von Document und DocumentFragment eingebunden wird. Hingegen ist querySelector eine Methode von ParentNode, ein Mixin, das von Document, DocumentFragment und Element inkludiert wird.

      Als Ergebnis sucht getElementById immer global im Dokument oder Fragment, während querySelector auf einen Element aufgerufen werden kann und dann auf dessen Scope beschränkt ist.

      Ob das merkliche Performance-Auswirkungen hat, glaube ich nicht. Die CSS Selectorengine dürfte jegliche Optimierung erhalten haben, die man sich nur vorstellen kann. In riesigen DOMs könnte das Suchen in einem kleineren Teilbaum vielleicht effizienter sein. Es könnte aber auch sein, dass getElementById durch eine Hashmap optimiert wird. Müsste man testen und messen.

      Funktionale Auswirkungen können sich ergeben,

      • wenn ich den Selektor #a im Scope eines bestimmten Elements suche, aber das das Element mit der ID a ist kein Kind des Scope-Elements, sondern anderswo im DOM (Parent, Sibling, x-th Cousin y-th removed - frag einen Hobbit…)
      • wenn ich ein ungültiges DOM mit nicht-eindeutigen IDs erstelle und das Problem damit „löse“, dass ich die uneindeutigen IDs im passenden Teilbaum suche, in dem sie eindeutig sind. Oder merkwürdige Abfragen wie querySelectorAll("#a") oder querySelector(".t #a") fabriziere.

      Das trifft auf die anderen Methoden nicht zu; die könnte man unter Sonstiges verbuchen.

      Muss man vielleicht auch im Nebensatz erwähnen, dass querySelectorAll eine statische Nodelist liefert, aber getElementsByTagName, getElementsByClassName und children eine live HTMLCollection, getElementsByName und childNodes jedoch eine live NodeList? Warum getElementsByName eine NodeList liefert, die beiden anderen getElements-Methoden hingegen eine HTMLCollection, ist vermutlich ein Artefakt der Browserkriege, aber es ist schon eine Inkonsistenz zum Verzeifeln.

      Hantieren mit live collections ist kritisch wenn man das DOM verändert. Ich würde auch wetten, dass ihre Verwendung Tempo kostet, weil sie bei jeder Verwendung neu erstellt werden müssen oder sie das DOM ständig auf Änderungen belauschen müssen. Hinzu kommt, dass man der NodeList-Schnittstelle die forEach-Methode spendiert hat und die Iterator-Quellen entries, keys und values, das würde ich als deutlichen Vorteil von querySelectorAll darstellen.

      Oh. Ups. HTMLCollection implementiert den Iterator neuerdings auch?! Is ja heiß…

      Gibt es Fälle, wo man unbedingt eine live-Collection aus Elementen braucht (von children und childNodes abgesehen)?

      Interessant ist auch dieser Stackoverflow-Beitrag von 2022, der etwas zur Historie der arrayoiden Listentypen im DOM verrät.

      Rolf

      --
      sumpsi - posui - obstruxi