Rolf B: click-within javascript test

Beitrag lesen

Hallo beatovich,

so ist's vielleicht schöner (hoffentlich auch korrekt... 😉 ).

document.body.addEventListener('click', function(ev){
 	for (var el=ev.target; el && el.NodeName != "BODY"; el=el.parentElement);
 		if(el.nodeName == "NAV") return;
  }
  closeNav();
});

Du hast damit bei jedem Klick eine Bottom-Up Suche durch das DOM. Da deine Seite keinen Weltrekord im Schnellklicken anstrebt, sollte das egal sein.

Man könnte auch einen Top-Down Ansatz wählen:

document.body.addEventListener('click', function(ev){
   var navList = document.getElementsByTagName("NAV");
   for (var i=0; i<navList.length; i++)
      if (navList[i].contains(ev.target) return;
   closeNav();
}

damit verwendest Du eine Menge nativer Browserfunktionen, aber ob das schneller ist als deine Parent-Suche? Keine Ahnung. Wenn contains schlau ist, sucht es vom übergebenen Node aufwärts bis es this oder null findet, und damit tut es das gleiche wie deine eigene Suche. Aber die Suche nach NAV Elementen dürfte eine DOM Traversierung bedeuten. D.h. die contains Idee lohnt nur wenn man genau ein nav hat, auf das reagiert werden soll.

Wenn Du keine Event-Propagierung stoppen willst, könntest Du vielleicht dennoch auf die Doppelregistrierung zurückgreifen. Der click-Handler auf <nav> könnte ein Extraproperty ans Event-Objekt kleben und der click-Handler am <body> darauf reagieren. Setzt natürlich voraus, dass nicht nachträglich nav-Elemente eingescriptet werden. Und wenn's mehr als ein nav geben können soll, muss die Registrierung für die nav-Listener in eine Schleife.

var hitNav = new Symbol();
var navList = document.getElementsByTagName("NAV");
for (var i=0; i<navList.length; i++) {
   navList[i].addEventListener('click', function(ev) {
      ev[hitNav] = true;
   });
}
document.body.addEventListener('click', function(ev) {
   if (ev[hitNav])
      return;
   closeNav();
});

Also Fazit: Wenn Du nur ein einziges NAV als relevant ansiehst, könnte die contains-Idee lohnen. Wenn's mehrere sind, bleib bei deiner Bottom-Up suche, die dürfte am performantesten sein. Kannst sie ja in einem Helper kapseln und den als ExtraFill ans HTMLElement kleben:

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

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

HTMLElement.prototype.closestParentWithTagName = function(name) {
   var el = this.parentElement;
   return (!el || el.tagName === name) ? el : el.closestParentWithTagName(name));
}

Rolf

--
sumpsi - posui - clusi