I begynnelsen av min reise som webutvikler, var et av de mest overraskende og fascinerende fenomenene jeg støtte på, hendelsesbobling. Først virket det litt rart, men når man forstår prinsippet bak det, gir det veldig mye mening. Som webutvikler vil du garantert komme over hendelsesbobling, så la oss se nærmere på hva det egentlig er.
JavaScript er avhengig av hendelser for å muliggjøre brukerinteraksjon med nettsider. En hendelse refererer til spesifikke hendelser eller handlinger som kan fanges opp og håndteres av koden du skriver. Dette kan inkludere museklikk, tastetrykk, innlevering av skjemaer, og mange andre typer interaksjoner.
For å oppdage og reagere på disse hendelsene, bruker JavaScript noe som kalles hendelseslyttere. En hendelseslytter er en funksjon som «lytter» eller venter på at en spesifikk hendelse skal inntreffe på en nettside, for eksempel et klikk på en knapp. Når en hendelseslytter registrerer den hendelsen den lytter etter, svarer den ved å utføre koden som er knyttet til den hendelsen. Hele denne prosessen med å oppdage og svare på hendelser kalles hendelseshåndtering.
La oss nå se for oss at vi har tre elementer på en nettside: en div, en span, og en knapp. Knappeelementet er plassert inne i span-elementet, og span-elementet er plassert inne i div-elementet. Illustrasjonen under viser dette:
Hvis vi antar at hvert av disse elementene har en hendelseslytter som er satt til å reagere på et klikk, og skrive noe til konsollen når det klikkes, hva skjer da når du klikker på knappen?
For å teste dette selv, kan du opprette en ny mappe. I denne mappen lager du en HTML-fil som heter index.html, en CSS-fil som heter style.css, og en JavaScript-fil som heter app.js.
I HTML-filen legger du inn følgende kode:
<html lang="no"> <head> <title>Hendelsesbobling</title> <link rel="stylesheet" href="style.css"> </head> <body> <div> <span><button>Klikk meg!</button></span> </div> <script src="app.js"></script> </body> </html>
I CSS-filen legger du til følgende kode for å style div- og span-elementet:
div { border: 2px solid black; background-color: orange; padding: 30px; width: 400px; } span { display: inline-block; background-color: cyan; height: 100px; width: 200px; margin: 10px; padding: 20px; border: 2px solid black; }
I JavaScript-filen legger du til koden under. Denne koden legger til hendelseslyttere for div-, span- og knappelementene, og alle lytter etter en klikkhendelse.
const div = document.querySelector('div'); div.addEventListener('click', () => { console.log("Du har klikket på et div-element"); }); const span = document.querySelector('span'); span.addEventListener('click', () => { console.log("Du har klikket på et span-element"); }); const button = document.querySelector('button'); button.addEventListener('click', () => { console.log("Du har klikket på en knapp"); });
Åpne nå HTML-filen i en nettleser. Åpne utviklerverktøyene i nettleseren (vanligvis ved å trykke F12) og klikk deretter på knappen på siden. Hva observerer du? Resultatet av å klikke på knappen vises nedenfor:
Når du klikker på knappen, utløses hendelseslytteren som lytter etter en klikkhendelse på knappen, og den skriver ut en melding i konsollen. Imidlertid utløses også hendelseslytterne på span- og div-elementene. Du lurer kanskje på hvorfor det er slik?
Når du klikker på knappen, utløses hendelseslytteren som er knyttet til knappen, som da skriver ut til konsollen. Men siden knappen er plassert inne i span-elementet, betyr det at å klikke på knappen teknisk sett også betyr at vi klikker på span-elementet. Derfor utløses også hendelseslytteren for span-elementet.
Siden span-elementet også er plassert inne i div-elementet, betyr det at å klikke på span-elementet også betyr at vi klikker på div-elementet, og som et resultat utløses også hendelseslytteren for div-elementet. Dette er essensen av hendelsesbobling.
Hendelsesbobling
Hendelsesbobling er en prosess der en hendelse som utløses i et HTML-element som er plassert inne i andre elementer, sprer seg eller «bobler opp» fra det innerste elementet der hendelsen oppstod, og beveger seg oppover i DOM-treet mot rotelementet. Dette fører til at alle hendelseslyttere som lytter etter den samme hendelsen i DOM-treet, blir utløst.
Hendelseslyttere blir utløst i en bestemt rekkefølge, som samsvarer med hvordan hendelsen bobler eller sprer seg oppover i DOM-treet. Se på DOM-treet under, som representerer strukturen til HTML-en som brukes i dette eksemplet.
En klikkhendelse som bobler opp i DOM-treet.
DOM-treet viser en knapp som er plassert inni et span-element, som igjen er plassert inni et div-element, som er inni body-elementet, som til slutt er inni HTML-elementet. Fordi elementene er plassert inne i hverandre, vil klikkhendelsen utløse hendelseslytteren som er knyttet til knappen.
Men siden elementene er plassert inne i hverandre, vil hendelsen fortsette oppover i DOM-treet (boble opp) til span-elementet, deretter div, deretter body og til slutt HTML-elementet, og utløse alle hendelseslyttere som lytter etter en klikkhendelse i den rekkefølgen.
Dette forklarer hvorfor hendelseslytterne som er knyttet til span- og div-elementene også utføres. Hvis vi hadde hatt hendelseslyttere som lyttet etter et klikk på body- og HTML-elementet, ville også disse blitt utløst.
DOM-noden der en hendelse inntreffer kalles målet. I vårt eksempel, siden klikket skjer på knappen, er knappelementet hendelsens mål.
Hvordan stoppe hendelsesbobling
For å stoppe en hendelse fra å boble oppover i DOM-treet, kan vi bruke en metode som heter stopPropagation() som er tilgjengelig på hendelsesobjektet. La oss se på kodeeksemplet under, som vi brukte for å legge til en hendelseslytter for knappelementet.
const button = document.querySelector('button'); button.addEventListener('click', () => { console.log("Du har klikket på en knapp"); });
Denne koden fører til at en hendelse bobler oppover i DOM-treet når en bruker klikker på knappen. For å forhindre at hendelsen bobler, kan vi kalle stopPropagation()-metoden som vist under:
const button = document.querySelector('button'); button.addEventListener('click', (e) => { console.log("Du har klikket på en knapp"); e.stopPropagation(); });
En hendelsesbehandler er funksjonen som utføres når en knapp klikkes. En hendelseslytter sender automatisk et hendelsesobjekt til en hendelsesbehandler. I vårt tilfelle er dette hendelsesobjektet representert av variabelnavnet e, som sendes som en parameter i hendelsesbehandleren.
Dette hendelsesobjektet, e, inneholder informasjon om hendelsen og gir oss også tilgang til en rekke egenskaper og metoder knyttet til hendelser. En slik metode er stopPropagation(), som brukes for å forhindre hendelsesbobling. Når vi kaller stopPropagation() i knappens hendelseslytter, hindrer vi at hendelsen bobler oppover i DOM-treet fra knappelementet.
Resultatet av å klikke på knappen etter at vi har lagt til stopPropagation()-metoden er vist under:
Vi bruker stopPropagation() for å hindre en hendelse i å boble fra elementet vi bruker den på. For eksempel, hvis vi ønsker at klikkhendelsen skal boble fra knappelementet opp til span-elementet, men ikke lenger opp i DOM-treet, kan vi bruke stopPropagation() i span-elementets hendelseslytter.
Hendelsesfanging
Hendelsesfanging er det motsatte av hendelsesbobling. Med hendelsesfanging «sildrer» en hendelse ned fra det ytterste elementet til elementet som er målet, som illustrert under:
Klikkhendelse sildrer ned til målelementet på grunn av hendelsesfangst.
For eksempel, i vårt tilfelle, når du klikker på knappeelementet med hendelsesfanging aktivert, vil hendelseslytterne på div-elementet bli utløst først. Deretter vil lytterne på span-elementet utløses, og til slutt vil lytterne på målelementet bli utløst.
Standardmåten hendelser spres i DOM er ved hendelsesbobling. For å endre denne standardoppførselen fra hendelsesbobling til hendelsesfanging, sender vi et tredje argument til våre hendelseslyttere for å sette hendelsesfanging til sann. Hvis du ikke sender inn et tredje argument til hendelseslytterne, vil hendelsesfanging settes til usann.
Se på hendelseslytteren under:
div.addEventListener('click', () => { console.log("Du har klikket på et div-element"); });
Siden det ikke er et tredje argument, er fanging satt til usann. For å sette fanging til sann, sender vi et tredje argument, den boolske verdien sann, som setter fanging til sann.
div.addEventListener('click', () => { console.log("Du har klikket på et div-element"); }, true);
Alternativt kan du sende inn et objekt som setter fanging til sant, som vist under:
div.addEventListener('click', () => { console.log("Du har klikket på et div-element"); }, {capture: true});
For å teste hendelsesfanging, kan du legge til et tredje argument til alle hendelseslyttere i JavaScript-filen din som vist under:
const div = document.querySelector('div'); div.addEventListener('click', () => { console.log("Du har klikket på et div-element"); }, true); const span = document.querySelector('span'); span.addEventListener('click', (e) => { console.log("Du har klikket på et span-element"); }, true); const button = document.querySelector('button'); button.addEventListener('click', () => { console.log("Du har klikket på en knapp"); }, true);
Åpne nå nettleseren din og klikk på knappen. Du burde få en utskrift som ligner på dette:
Legg merke til at i motsetning til hendelsesbobling, der utskriften fra knappen ble skrevet først, er den første utskriften ved hendelsesfanging fra det ytterste elementet, div.
Hendelsesbobling og hendelsesfanging er de to måtene hendelser spres i DOM. Det er derimot hendelsesbobling som er den vanligste måten å spre hendelser på.
Hendelsesdelegering
Hendelsesdelegering er et designmønster der man knytter en enkelt hendelseslytter til et felles overordnet element, som for eksempel et
- -element, i stedet for å legge til separate hendelseslyttere på hvert enkelt underelement. Hendelser på underelementer vil da «boble opp» til det overordnede elementet, der de blir håndtert av hendelsesbehandleren som er knyttet til det overordnede elementet.
Derfor, i et tilfelle der vi har et overordnet element med underordnede elementer plassert inne i det, legger vi bare til en hendelseslytter til det overordnede elementet. Denne hendelsesbehandleren vil da håndtere alle hendelsene som skjer i de underordnede elementene.
Du lurer kanskje på hvordan det overordnede elementet vil vite hvilket underordnet element som ble klikket. Som nevnt tidligere, sender en hendelseslytter et hendelsesobjekt til en hendelsesbehandler. Dette hendelsesobjektet inneholder metoder og egenskaper som tilbyr informasjon om den spesifikke hendelsen. En av egenskapene i hendelsesobjektet er «target»-egenskapen. «Target»-egenskapen peker til det spesifikke HTML-elementet der hendelsen skjedde.
For eksempel, hvis vi har en uordnet liste med listeelementer og vi knytter en hendelseslytter til
- -elementet, når en hendelse skjer på et listeelement, vil «target»-egenskapen i hendelsesobjektet peke til det spesifikke listeelementet der hendelsen skjedde.
For å se hendelsesdelegering i praksis, kan du legge til følgende HTML-kode i din eksisterende HTML-fil:
<ul> <li>Toyota</li> <li>Subaru</li> <li>Honda</li> <li>Hyundai</li> <li>Chevrolet</li> <li>Kia</li> </ul>
Legg til følgende JavaScript-kode for å bruke hendelsesdelegering til å bruke en enkelt hendelseslytter på et overordnet element for å lytte etter hendelser i de underordnede elementene:
const ul = document.querySelector('ul'); ul.addEventListener('click', (e) => { // target element targetElement = e.target // log ut innholdet til target element console.log(targetElement.textContent); });
Åpne nå nettleseren din og klikk på et hvilket som helst element i listen. Innholdet i elementet vil bli skrevet ut i konsollen som vist under:
Ved å bruke en enkelt hendelseslytter kan vi håndtere hendelser i alle underelementene. Å ha mange hendelseslyttere på en side påvirker ytelsen, bruker mer minne og fører til tregere lasting og gjengivelse av sider.
Hendelsesdelegering lar oss unngå alt dette ved å minimere antall hendelseslyttere vi må bruke på en side. Hendelsesdelegering er avhengig av hendelsesbobling. Derfor kan vi si at hendelsesbobling kan bidra til å optimalisere ytelsen til nettsider.
Tips for effektiv hendelseshåndtering
Som utvikler, når du arbeider med hendelser i Document Object Model (DOM), bør du vurdere å bruke hendelsesdelegering istedenfor å ha mange hendelseslyttere på elementer på siden din.
Når du bruker hendelsesdelegering, bør du huske å knytte en hendelseslytter til nærmeste felles foreldrelement til de underordnede elementene som trenger hendelseshåndtering. Dette hjelper med å optimalisere hendelsesboblingen og minimere avstanden en hendelse må reise før den blir håndtert.
Når du håndterer hendelser, bør du bruke hendelsesobjektet som er tilgjengelig via hendelseslytteren. Hendelsesobjektet inneholder egenskaper som «target», som er nyttige når du håndterer hendelser.
For å oppnå mer effektive nettsider bør du unngå overdreven DOM-manipulasjon. Hendelser som utløser hyppige DOM-manipulasjoner kan ha en negativ innvirkning på ytelsen til nettstedet ditt.
Til slutt, når det gjelder elementer som er plassert inne i andre elementer, må du være svært forsiktig når du legger til flere hendelseslyttere til elementene. Dette kan ikke bare påvirke ytelsen, men også gjøre håndteringen av hendelser mer komplisert og gjøre det vanskeligere å vedlikeholde koden.
Konklusjon
Hendelser er et kraftig verktøy i JavaScript. Hendelsesbobling, hendelsesfanging og hendelsesdelegering er viktige verktøy for å håndtere hendelser i JavaScript. Som webutvikler kan du bruke denne artikkelen til å bli bedre kjent med disse konseptene slik at du kan bygge mer interaktive, dynamiske og effektive nettsteder og applikasjoner.