JavaScript-evenementen van de grond af

Bijna alles wat u in JavaScript doet, zou moeten beginnen wanneer er iets gebeurt: de gebruiker klikt op een knop, zweeft over een menu, drukt op een toets, u krijgt het idee. Het is vrij eenvoudig te doen met bibliotheken zoals jQuery, maar weet je hoe je evenementen in raw JavaScript moet aansluiten? Vandaag zal ik je dat precies leren!


Welkom bij JavaScript-evenementen

JavaScript-evenementen afhandelen is geen rocket science. Natuurlijk zijn er browser verschillen (wat had je verwacht?), Maar als je het eenmaal onder de knie hebt en je eigen helper-functies hebt gebouwd om zelfs het speelveld te verbeteren, zul je blij zijn dat je het weet. Zelfs als je nog steeds van plan bent om een ​​bibliotheek voor dit soort dingen te gebruiken, is het goed om te weten wat er zich onder de oppervlakte afspeelt. Daar gaan we.


Level 0 Gebeurtenisafhandeling

De meest elementaire gebeurtenisafhandeling in JavaScript is gebeurtenisafhandeling op niveau 0. Het is vrij eenvoudig: wijs gewoon je functie toe aan de eigenschap event van het element. Let op:

 var element = document.getElementById ('myElement'); element.onclick = function () alert ('uw functie hier'); ;

Zodra ik het element heb, stel ik de bij klikken eigenschap gelijk aan een functie. Nu wanneer element is geklikt, die functie zal worden uitgevoerd en we zullen een waarschuwingsvenster zien.
Maar hoe wist ik het te gebruiken? bij klikken? Je kunt een lijst met beschikbare evenementen krijgen op Wikipedia. Het mooie aan de methode Level 0 is dat deze wordt ondersteund in alle browsers die tegenwoordig veel worden gebruikt. Het nadeel is dat je elke gebeurtenis maar één keer met elk element kunt registreren. Dit veroorzaakt dus maar één melding:

 element.onclick = function () alert ('function 1'); ; element.onclick = function () alert ('I overschrijf functie 1');

Alleen de tweede functie wordt uitgevoerd wanneer op het element wordt geklikt, omdat we de eigenschap onclick opnieuw hebben toegewezen. Om een ​​gebeurtenishandler te verwijderen, kunnen we deze gewoon op nul zetten.

 element.onclick = null;

Hoewel het alleen basisfunctionaliteit biedt, zorgt de consistente ondersteuning in alle browsers ervoor dat gebeurtenis 0 van niveau 1 een geldige optie is voor supereenvoudige projecten.


Volledige screencast



Het gebeurtenisobject en dit

Nu je JavaScript-gebeurtenissen opwarmt, bespreken we twee belangrijke facetten voordat we overgaan op betere technieken: het gebeurtenisobject en het deze trefwoord. Wanneer een gebeurtenis wordt uitgevoerd, is er veel informatie over de gebeurtenis die u misschien wilt weten: welke toets is ingedrukt? op welk element is geklikt? heeft de gebruiker met de linker-, rechter- of middelste knop geklikt? Al deze gegevens en meer worden opgeslagen in een gebeurtenisobject. Voor alle browsers behalve Internet Explorer kunt u bij dit object komen door het door te geven als een parameter voor de verwerkingsfunctie. In IE kun je het krijgen van de globale variabele window.event. Het is niet moeilijk om dit verschil op te lossen.

 function myFunction (evt) evt = evt || window.event; / * ... * / element.onclick = myFunction;

myFunction neemt de parameter event voor alle goede browsers en we wijzen deze toe aan window.event als evt bestaat niet We zullen een paar later naar enkele eigenschappen van het gebeurtenisobject kijken.

De deze keywork is belangrijk binnen event-handling-functies; het verwijst naar het element dat de gebeurtenis heeft ontvangen.

 element.onclick = function () this.style.backgroundColor = 'red'; // same als element.style.backgroundColor = 'red'; 

Helaas, in Internet Explorer, deze verwijst niet naar het geacteerde element; het verwijst naar het vensterobject. Dat is nutteloos, maar we zullen een manier bekijken om dat in een beetje te verhelpen.


Eventafhandeling niveau 2

Behandeling van gebeurtenissen op niveau 2 is de W3C-specificatie voor DOM-evenementen. Het is vrij simpel, eigenlijk.

 element.addEventListener ('mouseover', myFunction, false);

De methode wordt genoemd addEventListener. De eerste parameter is het type evenement waarnaar we willen luisteren. Meestal is het dezelfde naam als de Level 0 -gebeurtenissen, zonder het voorvoegsel 'aan'. De tweede parameter is een functienaam of de functie zelf. De laatste parameter bepaalt of we vastleggen of niet; we zullen dit in een moment bespreken.
Een van de grote voordelen van het gebruik addEventListener is dat we nu meerdere gebeurtenissen kunnen toewijzen voor een enkele gebeurtenis op een enkel element:

 element.addEventListener ('dblclick', logSuccessToConsole, false); element.addEventListener ('dblclick', function () console.log ('deze functie doens \' t negeer de eerste. ');, false);

Gebruik deze functie om deze handlers te verwijderen removeEventListener. Dus, om de functie die we hierboven hebben toegevoegd te verwijderen, doen we dit:

 element.removeEventListener ('dblclick', logSuccessToConsole, false);

Vastleggen en bubbelen

De laatste parameter in addEventListner en removeEventListener is een boolean die bepaalt of we de gebeurtenis in de vastleggende of borrelende fase starten. Dit is wat dat betekent:
Wanneer u op een element klikt, klikt u ook op elk van de voorouderelementen.

  
Werk opslaan

Als ik op de overspanning # besparen, Ik heb ook geklikt div # toolbar, div # wrapper, en lichaam. Als een van deze elementen gebeurtenislisteners heeft die naar klikken luisteren, worden hun respectieve functies gebeld. Maar welke volgorde moeten ze worden genoemd? Dat is wat de laatste parameter beslist. In de W3C-specificatie zou ons klikgebeurtenis beginnen bij de lichaam en ga door de ketting tot het de overspanning # besparen; dat is de fase van het vastleggen. Dan gaat het van de overspanning # besparen terug naar de lichaam; dat is het bubbelende stadium. Dit is gemakkelijk te onthouden, omdat luchtbellen naar boven drijven. Niet alle evenementen bubbelen; dat Wikipedia-diagram u zal vertellen welke doen en welke niet.

Dus als de capture-parameter is ingesteld op waar, de gebeurtenis wordt onderweg naar beneden geactiveerd; als het is ingesteld op vals, het wordt geactiveerd op de weg naar boven.

Het is je misschien al opgevallen dat tot nu toe al mijn voorbeelden het vastleggen op false hebben vastgelegd, dus de gebeurtenis wordt getriggerd in het bubblingstadium. Dit komt omdat Internet Explorer geen capture-fase heeft. In IE borrelen alleen evenementen.


Internet Explorer Event Model

IE heeft zijn eigen, eigen manier van werken met evenementen. Het gebruikt attachEvent.

 element.attachEvent ('onclick', runThisFunction);

Omdat IE-gebeurtenissen alleen bubbelen, hebt u de derde variabele niet nodig. De eerste twee zijn hetzelfde als met addEventListener, met de grote uitzondering dat IE dat 'op'-voorvoegsel voor het gebeurtenistype vereist. Natuurlijk kunt u met dit model meerdere gebeurtenissen van één soort aan een individueel element toewijzen. Gebruik. Om gebeurtenissen te verwijderen detachEvent.

 element.detachEvent ('onclick', runThisFunction);

Tijdelijk terug naar Bubbling

Wat als uw elementen niet willen dat hun ouders te weten komen over hun evenementen? Het is niet moeilijk om je te verstoppen. In IE, gebruikt u de cancelBubble eigenschap op het gebeurtenisobject die wordt doorgegeven aan de opgeroepen functie. Voor de rest kunt u de stopPropogation methode, ook op het gebeurtenisobject.

 e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation (); 

De stopPropogation methode zal ook gebeurtenissen in hun tracks stoppen tijdens de opnamefase.


Een paar eigenschappen van het gebeurtenisobject

Ik durf te wedden dat de meeste eigenschappen van het evenementobject ongebruikt blijven, maar er zijn een paar geselecteerde die u van onschatbare waarde vindt. Laten we die paar bekijken.


doelwit

De doelwit eigenschap is het diepste element waarop is geklikt. In de bovenstaande afbeelding kunt u zien dat ik heb geklikt h1 # toets. In IE wordt deze eigenschap genoemd srcElement; om bij deze woning te komen, doe zoiets als dit:

 var target = e.target || e.srcElement;

currentTarget

De currentTarget property is het element dat de gebeurtenis nu heeft. In die schermafbeelding is de currentTarget is div # wrapper. Dit betekent dat ik heb geklikt h1 # toets, maar het gebeurtenisobject is in de console gelogd in de functie die wordt aangeroepen door de div # wrapperde gebeurtenis luisteraar.

Maar dit zal je voor een lus gooien: IE heeft geen currentTarget-eigenschap, of iets dergelijks. Herinnerend aan het feit dat IE niet eens verhuurt deze gelijk aan het element dat de gebeurtenis activeert, dit betekent dat we geen mogelijkheid hebben om het element te krijgen dat de gebeurtenis heeft geactiveerd toen het opborrelt (wat betekent dat er op een afstammeling van het element is geklikt). Verward? Misschien helpt een voorbeeld: als we in Internet Explorer deze HTML hebben ...

 
Knop

... en deze gebeurtenishandler ...

 var par = document.getElementById ('parent'); parent.attachEvent ('onclick', doSomething);

... dan, wanneer we op klikken overspanning # kind en het evenement bubbelt omhoog naar div # ouder, we kunnen niet van binnen uit de functie komen doe iets om bij het element te komen (in dit geval, div # ouder) het evenement is geactiveerd.

anderen

Er zijn andere eigenschappen in het gebeurtenisobject die u handig vindt, afhankelijk van de situatie. Op een toetsenbordgebeurtenis, (keyup, keydown, keypress) zul je de sleutelcode en de charCode. U kunt zien of de gebruiker de alt-, shift- of ctrl-toets (cmd) ingedrukt hield terwijl u op / klikte. Verken de gebeurtenisobjecten van verschillende evenementen en zie waar je mee moet werken!


Gebeurtenissen repareren

We zijn tot nu toe twee hoofdproblemen tegengekomen bij onze studie van evenementen.

  • IE gebruikt een ander model dan de andere browsers.
  • IE heeft geen toegang tot het bovenliggende object van de gebeurtenis; het mist de currentTarget eigendom, en de deze object verwijst naar het vensterobject.

Om deze problemen op te lossen, bouwen we een twee functies met ondersteuning voor meerdere browsers, een om gebeurtenissen te koppelen en een om ze te verwijderen.

We beginnen met Voeg evenement toe; hier is de eerste ronde:

 var addEvent = function (element, type, fn) if (element.attachEvent) element.attachEvent ('on' + type, fn);  else element.addEventListener (type, fn, false); 

Ik draag een anonieme functie toe aan de variabele Voeg evenement toe om redenen die u zo meteen zult zien. Het enige wat we moeten doen is controleren om te zien of het element de attachEvent methode. Als dat zo is, bevinden we ons in IE en gebruiken we die methode. Zo niet, dan kunnen we gebruiken addEventListener.

Dit lost echter niet het feit op dat we geen toegang hebben tot het hoofdelement van de gebeurtenis in IE. Om dit te doen, zullen we de functie een attribuut maken op het element; op die manier wordt het waargenomen als een methode van het element, en deze zal het element evenaren. Ja, ik weet het, briljant, is het niet? Nou, ik kan geen enkele eer aan: dit idee komt van een blogpost van John Resig.

 var addEvent = function (element, type, fn) if (element.attachEvent) element ['e' + type + fn] = fn; element [type + fn] = function () element ['e' + type + fn] (window.event); element.attachEvent ('on' + type, element [type + fn]);  else element.addEventListener (type, fn, false); 

De eerste extra regel (element ['e' + type + fn] = fn;) maakt de functie een methode van het element, dus dat deze zal nu het element evenaren. De tweede regel is een functie die de methode uitvoert en in het window.event-object doorgeeft. Dit bespaart u de extra stap van het controleren van de parameter van het gebeurtenisobject binnen uw functie. Merk ten slotte op dat we de functieparameter hebben gewijzigd in attachEvent methode om element [Type fn +].

Dat lost onze twee problemen op. We kunnen nu onze gebruiken Voeg evenement toe functie in alle browsers met een zo veel mogelijk vergelijkbare ervaring. Maar er is een kleine optimalisatie die we graag willen maken.

Merk op dat elke keer onze Voeg evenement toe functie wordt aangeroepen, wordt er gecontroleerd op de attachEvent methode. Er is geen reden waarom we dit meer dan eens zouden moeten controleren, ongeacht hoeveel evenementen we willen registreren. We kunnen een handige truc gebruiken waarmee we opnieuw kunnen schrijven Voeg evenement toe.

 var addEvent = function (el, type, fn) if (el.attachEvent) addEvent = function (el, type, fn) el ['e' + type + fn] = fn; el [type + fn] = functie () el ['e' + type + fn] (window.event); ; el.attachEvent ('on' + type, el [type + fn]);  else addEvent = function (el, type, fn) el.addEventListener (type, fn, false);  addEvent (el, type, fn); 

Nu, als het Voeg evenement toe methode is gevonden, we zullen opnieuw schrijven Voeg evenement toe om alleen de IE-onderdelen op te nemen; zo niet, dan nemen we gewoon de goede browseronderdelen op.
Onze removeEvent functie zal erg op elkaar lijken:

 var removeEvent = function (element, type, fn) if (element.detachEvent) removeEvent = function (element, type, fn) element.detachEvent ('on' + type, el [type + fn]); element [type + fn] = null;  else removeEvent = function (element, type, fn) element.removeEventListener (type, fn, false);  removeEvent (element, type, fn); 

Dat is het voor onze event-functies voor cross-browsers. Er is echter één ding dat ik niet leuk aan hen vind: de functies zijn geen methoden van de elementen; in plaats daarvan is het element een parameter. Ik weet dat het gewoon een kwestie van stijl is, maar ik vind het leuk

 btn.addEvent ('klik', doe dit);

beter dan

 addEvent (btn, 'klik', doThis);

We kunnen dit doen door onze functies hierin in te pakken:

 var E = Element.prototype; E.addEvent = function (type, fn) addEvent (this, type, fn);  E.removeEvent = function (type, fn) removeEvent (this, type, fn); 

Dit werkt in alle moderne browsers, evenals IE8; IE7 en IE6 stikken in Element. Het hangt van je publiek af; graag of niet!


Evenement delegatie

Nu dat je alles weet over JavaScript-evenementen, haast je niet en vul je volgende project met gebeurtenislisteners. Als je veel elementen hebt die reageren op hetzelfde type evenement (zeg bijvoorbeeld klik), is het verstandiger om te profiteren van borrelen. U kunt gewoon een gebeurtenislistener toevoegen aan een voorouderelement, zelfs het hoofdgedeelte, en de eigenschap target / srcElement op het gebeurtenisobject gebruiken om erachter te komen op welk dieper element is geklikt. Dit wordt gebeurtenisdelegatie genoemd. Hier is een nutteloos voorbeeld:

Hier is wat HTML:

 
  • huis
  • portefeuille
  • wat betreft
  • contact

We kunnen afspraakafgifte gebruiken om klikken naar elk te verwerken li:

 functie navigatie (e) var el = e.target || e.srcElement; switch (el.id) case 'home': alert ('go home'); breken; case 'portfolio': alert ('bekijk mijn producten'); breken; case 'about': alert ('al die walgelijke details'); breken; case 'contact': alert ('Ik ben gevleid dat je graag wilt praten'); breken; standaard: waarschuwing ('hoe ben je hier terechtgekomen?');  var nav = document.getElementById ('nav'); addEvent (nav, 'klik', navigatie);

We gebruiken een switch / case-statement om naar de id van het dieper geklikte element te kijken. Vervolgens doen we iets dienovereenkomstig.


Conclusie

Dus nu kun je JavaScript-evenementen gebruiken met de beste ervan. Veel plezier ermee en stel vragen in de opmerkingen!