JavaScript-hendelser Fra Ground Up

Nesten alt du gjør i JavaScript, skal begynne når noe skjer: brukeren klikker på en knapp, svinger over en meny, trykker på en tast, du får ideen. Det er ganske enkelt å gjøre med biblioteker som jQuery, men vet du hvordan du kobler opp hendelser i rå JavaScript? I dag skal jeg bare lære deg det!


Velkommen til JavaScript-hendelser

Håndtering av JavaScript-hendelser er ikke rakettvitenskap. Selvfølgelig er det nettleserforskjeller (hva forventet du?), Men når du får tak i det og bygger dine egne hjelpefunksjoner til enda lekeplassen, vil du være glad du vet det. Selv om du fortsatt planlegger å bruke et bibliotek for denne typen ting, er det godt å vite hva som skjer under overflaten. Her går vi.


Nivå 0 Hendelseshåndtering

Den mest grunnleggende hendelseshåndteringen i JavaScript er Nivå 0 hendelseshåndtering. Det er ganske enkelt: Tilordne bare funksjonen til hendelsesegenskapen på elementet. Observere:

 var element = document.getElementById ('myElement'); element.onclick = function () alert ('din funksjon her'); ;

Når jeg har elementet, setter jeg ved trykk eiendom som er lik en funksjon. Nå, når element er klikket, vil denne funksjonen løpe, og vi får se en advarselsboks.
Men hvordan visste jeg å bruke ved trykk? Du kan få en liste over tilgjengelige hendelser på Wikipedia. Skjønnheten om nivå 0-metoden er at den støttes på alle nettlesere som brukes mye i dag. Ulempen er at du kun kan registrere hver hendelse en gang med hvert element. Så dette vil bare føre til en advarsel:

 element.onclick = funksjon () alarm ('funksjon 1'); ; element.onclick = function () alert ('Jeg overstyrer funksjon 1');

Bare den andre funksjonen vil kjøre når elementet klikkes, fordi vi har tilordnet den onclick-egenskapen. For å fjerne en hendelseshåndterer, kan vi bare sette den til null.

 element.onclick = null;

Selv om det bare gir grunnleggende funksjonalitet, gjør konsistent støtte over alle nettlesere Nivå 0-hendelseshåndtering et gyldig alternativ for super enkle prosjekter.


Full Screencast



Hendelsesobjektet og dette

Nå som du varmer opp i JavaScript-hendelser, la oss diskutere to viktige fasetter før du går videre til bedre teknikker: hendelsesobjektet og dette søkeord. Når en hendelse blir utført, er det mye informasjon om hendelsen du kanskje vil vite: hvilken nøkkel ble presset? hvilket element ble klikket? klarte brukeren med venstre, høyre eller mellomknappen? Alle disse dataene og mer lagres i et hendelsesobjekt. For alle nettlesere unntatt Internet Explorer, kan du komme til dette objektet ved å sende det som en parameter til håndteringsfunksjonen. I IE kan du få det fra den globale variabelen window.event. Det er ikke vanskelig å løse denne forskjellen.

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

myFunction tar hendelsesparameteren for alle gode nettlesere, og vi overfører den til window.event hvis evt eksisterer ikke. Vi ser på noen av egenskapene til hendelsesobjektet litt senere.

De dette nøkkelarbeid er viktig innen hendelseshåndteringsfunksjoner; det refererer til elementet som mottok arrangementet.

 element.onclick = funksjon () this.style.backgroundColor = 'rød'; // samme som element.style.backgroundColor = 'rød'; 

Dessverre, i Internet Explorer, dette refererer ikke til det oppførte elementet; det refererer til vinduet objektet. Det er ubrukelig, men vi ser på en måte å avhjelpe det på litt.


Nivå 2 Hendelsehåndtering

Nivå 2 hendelseshåndtering er W3C-spesifikasjonen for DOM-hendelser. Det er ganske enkelt, faktisk.

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

Metoden kalles addEventListener. Den første parameteren er den type arrangement vi vil lytte til. Vanligvis er det samme navn som nivå 0-hendelsene, uten 'på' prefiks. Den andre parameteren er enten et funksjonsnavn eller selve funksjonen. Den endelige parameteren bestemmer om vi tar opp eller ikke; Vi diskuterer dette på et øyeblikk.
En av de store fordelene ved å bruke addEventListener er at vi nå kan tilordne flere hendelser for en enkelt hendelse på et enkelt element:

 element.addEventListener ('dblclick', logSuccessToConsole, false); element.addEventListener ('dblclick', funksjon () console.log ('denne funksjonen overstyrer ikke den første.');, false);

For å fjerne disse håndteringene, bruk funksjonen removeEventListener. Så, for å fjerne funksjonen vi la til ovenfor, gjør vi dette:

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

Fanger og bobler

Den endelige parameteren i addEventListner og removeEventListener er en boolsk som definerer om vi brann hendelsen i fangst eller boblende scenen. Her er hva det betyr:
Når du klikker på et element, klikker du også på hver av sine forfedreelementer.

  
Lagre arbeid

Hvis jeg klikker på span # spare, Jeg har også klikket div # verktøylinje, div # wrapper, og kropp. Så hvis noen av disse elementene har hendelseslyttere som lytter etter klikk, blir deres respektive funksjoner kalt. Men hvilken rekkefølge skal de bli innkalt? Det er hva den siste parameteren bestemmer. I W3C-spesifikasjonen ville vår klikkhendelse starte på kropp og flytt ned kjeden til den når span # spare; det er fangststadiet. Så går det fra span # spare sikkerhetskopiere til kropp; det er det boblende scenen. Dette er lett å huske, fordi bobler flyter oppover. Ikke alle hendelser boble; at Wikipedia-diagrammet vil fortelle deg hvilke som gjør og hvilke som ikke gjør det.

Så hvis fangstparameteren er satt til ekte, hendelsen vil bli utløst på vei ned; hvis det er satt til falsk, det vil bli utløst på vei opp.

Du har kanskje lagt merke til at så langt har alle mine eksempler satt til å falle, så hendelsen utløses i boblende scenen. Dette skyldes at Internet Explorer ikke har en fangstfase. I IE, hendelser bare boble.


Internet Explorer Event Model

IE har sin egen, proprietære måte å jobbe med hendelser på. Det bruker attachEvent.

 element.attachEvent ('onclick', runThisFunction);

Siden IE-hendelser bare bobler, trenger du ikke den tredje variabelen. De to første er de samme som med addEventListener, med det store unntaket at IE krever at "på" prefiks for arrangementstypen. Selvfølgelig kan du tilordne flere hendelser av samme type til et enkelt element med denne modellen. For å fjerne hendelser, bruk detachEvent.

 element.detachEvent ('onclick', runThisFunction);

Øyeblikkelig tilbake til bubbling

Hva om elementene dine ikke vil at foreldrene skal finne ut om hendelsene deres? Det er ikke vanskelig å gjemme seg. I IE bruker du cancelBubble eiendom på hendelsesobjektet som blir sendt til den oppkalte funksjonen. For resten kan du bruke stopPropogation metode, også på arrangementet objektet.

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

De stopPropogation Metoden vil også stoppe hendelser i sporene under fangststadiet.


Et par egenskaper av hendelsesobjektet

Jeg ville være villig til å satse på at de fleste eiendommer på arrangementet vil gå ubrukt, men det er noen få utvalgte som du har funnet uvurderlig. La oss se på de få.


mål

De mål Egenskapen er det dypeste elementet som ble klikket. I bildet ovenfor kan du se at jeg klikket h1 # -knapp. I IE kalles denne eiendommen srcElement; å komme til denne eiendommen, gjør noe slikt:

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

currentTarget

De currentTarget eiendom er elementet som har hendelsen akkurat nå. I det skjermbildet, den currentTarget er div # wrapper. Dette betyr at jeg klikket h1 # -knapp, men hendelsesobjektet ble logget på konsollen i funksjonen som ble kalt av div # wrapperhendelse lytter.

Men dette vil kaste deg for en loop: IE har ikke en currentTarget-eiendom, eller noe som helst. Minnes om at IE ikke engang lar dette lik elementet som utløser hendelsen, betyr det at vi ikke har mulighet til å få elementet som utløste hendelsen når det bobler opp (noe som betyr at en etterkommer av elementet ble klikket). Forvirret? Kanskje et eksempel vil hjelpe: hvis i Internet Explorer har vi denne HTML-koden ...

 
Knapp

... og denne hendelseshandleren ...

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

... da, når vi klikker på span # barn og hendelsen bobler opp til div # forelder, Vi har ingen måte fra innsiden av funksjonen gjør noe å komme på elementet (i dette tilfellet, div # forelder) hendelsen ble utløst på.

andre

Det finnes andre egenskaper på hendelsesobjektet som du finner nyttige, avhengig av saken. På et tastaturhendelse, (tast opp, keydown, tastetrykk), får du den nøkkelkode og charCode. Du vil kunne fortelle om brukeren holdt alt, skift eller ctrl (cmd) -tasten mens du trykker / klikker. Utforsk begivenhetsobjekter av ulike hendelser og se hva du må jobbe med!


Fastsetting av hendelser

Vi har kommet over to hovedproblemer i vår studie av hendelser så langt.

  • IE bruker en annen modell enn de andre nettleserne.
  • IE har ingen måte å få tilgang til arrangementets foreldreobjekt; det mangler currentTarget eiendom og dette objekt refererer til vinduet objektet.

For å løse disse problemene, la oss bygge to funksjoner med kryssbrowser-støtte, en for å koble til hendelser og en for å fjerne dem.

Vi begynner med addEvent; her er rundt en:

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

Jeg tilordner en anonym funksjon til variabelen addEvent av grunner vil du se om et minutt. Alt vi trenger å gjøre er å se om elementet har attachEvent metode. Hvis det gjør det, er vi i IE, og vi bruker den metoden. Hvis ikke, kan vi bruke addEventListener.

Dette gjør imidlertid ikke det faktum at vi ikke har mulighet til å få tilgang til arrangementets foreldreelement i IE. For å gjøre dette, gjør vi funksjonen et attributt på elementet; På den måten oppfattes det som en metode for elementet, og dette vil være lik elementet. Ja, jeg vet, strålende, er det ikke? Vel, jeg kan ikke ta noen kreditt: Denne ideen kommer fra et blogginnlegg av John Resig.

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

Den første ekstra linjen (element ['e' + type + fn] = fn;) gjør funksjonen til en metode for elementet, slik at dette vil nå lik elementet. Den andre linjen er en funksjon som utfører metoden, passerer i window.event-objektet. Dette sparer deg for ekstra trinn for å sjekke eventelementparameteren i funksjonen. Endelig merk at vi har endret funksjonsparameteren i attachEvent metode til element [Type + fn].

Det løser våre to problemer. Vi kan nå bruke vår addEvent Fungerer i alle nettlesere med så mye av en lignende opplevelse som mulig. Men det er litt optimzation som vi ønsker å lage.

Legg merke til at hver gang vår addEvent funksjonen kalles, den sjekker for attachEvent metode. Det er ingen grunn til at vi bør se etter dette mer enn en gang, uansett hvor mange hendelser vi ønsker å registrere. Vi kan bruke et greit triks som gjør at vi kan skrive på nytt addEvent.

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

Nå, hvis addEvent Metoden er funnet, vi skriver om igjen addEvent å bare inkludere IE-delene; Hvis ikke, vil vi bare inkludere de gode nettleserne.
Våre removeEvent funksjonen vil være veldig lik:

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

Det er det for våre funksjonsfunksjoner på tvers av nettlesere. Det er en ting jeg ikke liker om dem, skjønt: funksjonene er ikke elementer av elementene; I stedet er elementet en parameter. Jeg vet at det bare er stil, men jeg liker det

 btn.addEvent ('klikk', gjør dette);

bedre enn

 addEvent (btn, 'klikk', gjør dette);

Vi kan gjøre dette ved å pakke inn våre funksjoner i dette:

 var E = Element.prototype; E.addEvent = funksjon (type, fn) addEvent (dette, type, fn);  E.removeEvent = funksjon (type, fn) removeEvent (dette, type, fn); 

Dette vil fungere i alle moderne nettlesere, i tillegg til IE8; IE7 og IE6 choke på Element. Det avhenger av publikum; ta det eller la det gå!


Hendelse delegering

Nå som du vet alt om JavaScript-hendelser, ikke haste ut og fyll ditt neste prosjekt med hendelseslyttere. Hvis du har mange elementer som svarer på samme type arrangement (si, klikk), er det klokere å dra nytte av boblende. Du kan bare legge til en hendelselytter til et forfødselselement, til og med kroppen, og bruk egenskapen target / srcElement på hendelseobjektet for å finne ut hvilket dypere element som ble klikket. Dette kalles Event Delegation. Her er et ubrukelig eksempel:

Her er noen HTML:

 
  • hjem
  • portefølje
  • handle om
  • ta kontakt med

Vi kan bruke hendelsesdelegasjonen til å håndtere klikk til hver enkelt li:

 funksjonsnavigering (e) var el = e.target || e.srcElement; bytte (el.id) case 'home': varsel ('gå hjem'); gå i stykker; saken 'portefølje': varsling ('sjekk ut mine produkter'); gå i stykker; saken 'om': varsel ('alle de motbydelige detaljer'); gå i stykker; saken 'kontakt': varsling ('Jeg er smigret du vil like å snakke'); gå i stykker; standard: varsel ('hvordan kom du hit?');  var nav = document.getElementById ('nav'); addEvent (nav, 'klikk', navigasjon);

Vi bruker en bryter / case-setning for å se på iden til det dypere klikket elementet. Da gjør vi noe tilsvarende.


Konklusjon

Så nå kan du bruke JavaScript-hendelser med det beste av dem. Ha det gøy med det, og still spørsmål i kommentarene!