Orlok: click-within javascript test

Beitrag lesen

Hey

HTMLElement.prototype.closestParentWithTagName = function(name) {
   var el = this.parentElement;
   while (el && el.tagName !== name);
        el = el.parentElement);
   return el;
}

Ich finde es irgendwie nicht besonders sinnvoll etwas nachzubauen, was in ähnlicher Form bereits standardisiert und in modernen Browsern implementiert ist.

Es wird ein Zeitpunkt kommen, an dem ein Polyfill für Element.prototype.closest wieder entfernt werden kann, und wenn es soweit ist, kann man das machen ohne den restlichen Code anfassen zu müssen. Bei einer selbstgebastelten Lösung müssten hingegen auch alle Aufrufe verändert werden, wenn man sich irgendwann entscheidet die native Methode zu nutzen.

Ich sehe auch nicht, dass die Methode gegenüber closest einen so gewichtigen Vorteil hätte, dass es gerechtfertigt wäre sie auch dann noch einzubinden, wenn die Unterstützung von closest durch die Browser längst kein Thema mehr ist. Irgendwann wird das dann nur noch mitgeschleppt, weil keiner Lust hat alle Stellen zu suchen an denen es verwendet wird.

   while (el && el.tagName !== name);
        el = el.parentElement);

Allerdings finde ich es gut, dass du hier nach el.parentElement noch eine schließende runde Klammer eingefügt hast. Das hätte sonst ins Auge gehen können. ;-)


Abgesehen von den bereits genannten, gibt es noch eine weitere Möglichkeit festzustellen, ob eine Aktivierung innerhalb oder außerhalb eines nav-Elementes erfolgt ist. Nämlich die Methode Event.composedPath, mittels derer man den jeweiligen Event Path eines Ereignisses lesen kann, also alle Objekte von window bis zum Zielknoten, die von dem Eventobjekt auf seinem Weg durch’s DOM passiert werden.

const nav = document.querySelector('nav')

document.body.addEventListener('click', event => event.composedPath().includes(nav) && console.info('click in nav'))

Das ist auch insofern interessant, als dass hier nicht – wie bei älteren DOM-Methoden üblich – eine HTMLCollection oder NodeList zurückgegeben wird, die kaum über nützliche Methoden verfügt, sondern stattdessen ein richtiges Array. Das heißt, man kann hier auch ohne vorheriges Umkopieren der Referenzen mit Methoden wie filter, find, oder auch includes auf der Liste operieren.


Und wenn wir schon dabei sind, noch eine besonders unhandliche Variante:

Die Methode Node.compareDocumentPosition(otherNode) gibt eine Bitmaske zurück, mit der sich unter anderem prüfen lässt, ob das Kontextobjekt und das als Argument übergebene Objekt Vor- oder Nachfahren sind. Damit könnte die Prüfung so aussehen…

const nav = document.querySelector('nav');


document.body.addEventListener('click', function({ target }) {

    if (nav.isSameNode(target) || nav.compareDocumentPosition(target) &
            Node.DOCUMENT_POSITION_CONTAINED_BY) {

        console.info('click in nav');
    }

});

…oder anders herum, mit Node.DOCUMENT_POSITION_CONTAINS. Statt die unnötig sperrigen Eigenschaften auf dem Objekt Node zu referenzieren, könnte man natürlich auch direkt mit 16 oder 8 verunden. So sieht dann gut lesbarer Code aus. :-D


Das geht auch rekursiv - und wenn JavaScript mal echte Tail-Rekursion kriegt, dürfte es sogar effizient werden

Zumindest sind Tail Position Calls seit ECMAScript 2015 im Standard. Es wird zunächst definiert, wann syntaktisch ein Tail Call vorliegt, und dann unter Runtime Semantics der konkreten Implementierung aufgetragen, alle mit dem aktuellen Funktionsausführungskontext verknüpften internen Ressourcen entweder freizugeben, oder sie für den neuen Aufruf wiederzuverwenden.

Die Browserunterstützung für Tail Call Optimization ist aber in der Tat noch nicht überwältigend. Allerdings ist die maximale Größe des Call Stacks aktueller Engines meines Wissens vergleichsweise komfortabel. Anders als zum Beispiel in Python, wo die Ausführung standardmäßig bei 1000 Aufrufen abgewürgt wird, hat man hier in der Regel größeren Spielraum. Also solange man nicht mit der Ackermannfunktion rechnet…

Viele Grüße,

Orlok