När jag fortfarande lärde mig linorna för webbutveckling, var ett av de överraskande och spännande beteenden jag stötte på händelsebubblande. Först verkade det ovanligt, men när du tänker på händelsen som bubblar, kommer du att se att det är så vettigt. Som webbutvecklare kommer du säkert att stöta på bubblande händelser. Så vad bubblar händelsen?
JavaScript förlitar sig på händelser för att ge användarna möjlighet att interagera med webbsidor. En händelse hänvisar till händelser eller åtgärder som kan upptäckas och besvaras av koden du skriver. Exempel på händelser inkluderar musklick, tangenttryckningar och formulärinlämning, bland annat.
JavaScript använder händelseavlyssnare för att upptäcka och svara på händelser. En händelseavlyssnare är en funktion som lyssnar efter eller väntar på att en specifik händelse inträffar på en sida. Till exempel en klickhändelse på en knapp. När en händelseavlyssnare upptäcker händelsen den lyssnar på, svarar den genom att exekvera kod som är associerad med händelsen. Hela denna process att fånga och svara på händelser kallas händelsehantering.
Föreställ dig nu att vi har tre element på en sida: en div, en span och en knapp. Knappelementet är kapslat inuti span-elementet, och span-elementet är kapslat inuti div. En illustration av detta visas nedan:
Förutsatt att vart och ett av dessa element har en händelseavlyssnare som lyssnar efter en klickhändelse och skriver ut till konsolen när den klickas, vad händer när du klickar på knappen?
För att testa detta själv, skapa en mapp och i mappen, skapa en HTML-fil som heter index.html, en CSS-fil som heter style.css och en JavaScript-fil som heter app.js.
Lägg till följande kod i HTML-filen:
<!DOCTYPE html> <html lang="en"> <head> <title>Event bubbling</title> <link rel="stylesheet" href="https://wilku.top/the-hidden-key-to-dynamic-web-interactions/style.css"> </head> <body> <div> <span><button>Click Me!</button></span> </div> <script src="app.js"></script> </body> </html>
Lägg till följande kod i CSS-filen för att utforma div- och 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; }
Lägg till följande kod i JavaScript-filen, som lägger till händelseavlyssnare till div-, span- och knappelementen. Eventlistarna lyssnar alla efter en klickhändelse.
const div = document.querySelector('div'); div.addEventListener('click', () => { console.log("You've clicked a div element") }) const span = document.querySelector('span'); span.addEventListener('click', () => { console.log("You've clicked a span element") }) const button = document.querySelector('button'); button.addEventListener('click', () => { console.log("You've clicked a button") })
Öppna nu HTML-filen i en webbläsare. Inspektera sidan och klicka sedan på knappen på sidan. Vad märker du? Resultatet av att klicka på knappen visas nedan:
Genom att klicka på knappen utlöses händelselyssnaren efter en klickhändelse på knappen. Emellertid triggas även händelselyssnarna på span- och div-elementen. Varför är det så, kan man fråga sig.
Genom att klicka på knappen utlöses händelseavlyssnaren som är kopplad till knappen, som skrivs ut till konsolen. Men eftersom knappen är kapslad inuti ett span-element, innebär ett klicka på knappen att vi tekniskt sett också klickar på span-elementet, och därför utlöses dess händelseavlyssnare.
Eftersom span-elementet också är kapslat inuti div-elementet, innebär ett klick på span-elementet också att div-elementet också klickas, och som ett resultat av detta triggas även dess händelseavlyssnare. Det här är en händelse som bubblar.
Innehållsförteckning
Event Bubbling
Händelsebubbling är den process genom vilken en händelse som utlöses i en kapslad uppsättning HTML-element sprids eller bubblar upp från det innersta elementet där den utlöstes och går upp i DOM-trädet till rotelementet, vilket utlöser alla händelseavlyssnare som lyssnar efter den händelsen.
Händelseavlyssnarna triggas i en specifik ordning som matchar hur händelsen bubblar eller sprider sig upp i DOM-trädet. Tänk på DOM-trädet som visas nedan, som representerar strukturen för HTML som används i den här artikeln.
En klickhändelse som bubblar upp i DOM-trädet
DOM-trädet visar en knapp, kapslad inuti ett span, som är kapslad inuti en div, som är kapslad inuti kroppen och kroppen är kapslad inuti HTML-elementet. Eftersom elementen har kapslats inuti varandra när du klickar på knappen, kommer klickhändelsen att utlösa händelseavlyssnaren som är kopplad till knappen.
Men eftersom elementen är kapslade, kommer händelsen att flytta uppåt i DOM-trädet (bubbla upp) till span-elementet, sedan div, sedan kroppen och slutligen HTML-elementet, vilket utlöser alla händelseavlyssnare som lyssnar efter en klickhändelse i den beställa.
Det är därför händelseavlyssnaren kopplad till span- och div-elementen exekveras. Om vi hade haft evenemangslyssnare som lyssnade efter ett klick på kroppen och HTML-elementet, skulle de också ha triggats.
DOM-noden där en händelse inträffar kallas ett mål. I vårt fall, eftersom klicket sker på knappen, är knappelementet händelsemålet.
Hur man stoppar händelsebubbling
För att stoppa en händelse från att bubbla upp DOM, använder vi en metod som heter stopPropagation() som är tillgänglig på händelseobjektet. Tänk på kodexemplet nedan, som vi använde för att lägga till en händelseavlyssnare till knappelementet.
const button = document.querySelector('button'); button.addEventListener('click', () => { console.log("You've clicked a button"); })
Koden leder till att en händelse bubblar upp i DOM-trädet när en användare klickar på knappen. För att förhindra händelsebubbling anropar vi metoden stopPropagation() som visas nedan:
const button = document.querySelector('button'); button.addEventListener('click', (e) => { console.log("You've clicked a button"); e.stopPropagation(); })
En händelsehanterare är den funktion som exekveras när en knapp klickas. En händelseavlyssnare skickar automatiskt ett händelseobjekt till en händelsehanterare. I vårt fall representeras detta händelseobjekt av variabelnamnet e, som skickas som en parameter i händelsehanteraren.
Detta händelseobjekt, e, innehåller information om en händelse och ger oss även tillgång till en mängd olika egenskaper och metoder relaterade till händelser. En sådan metod är stopPropagation(), som används för att förhindra händelsebubbling. Att anropa stopPropagation() i knappens händelseavlyssnare förhindrar att en händelse bubblar upp i DOM-trädet från knappelementet.
Resultatet av att klicka på knappen efter att ha lagt till metoden stopPropagation() visas nedan:
Vi använder stopPropagation() för att förhindra att en händelse bubblar upp från elementet vi använder den på. Om vi till exempel ville att klickhändelsen skulle bubbla upp från knappelementet upp till span-elementet och inte bubbla längre upp i DOM-trädet, skulle vi använda stopPropagation() på spans händelseavlyssnare.
Händelse fånga
Att fånga händelser är motsatsen till händelsebubbling. Vid händelsefångst sipprar en händelse ner från det yttersta elementet till målelementet som illustreras nedan:
Klickahändelse sipprar ner till målelementet på grund av händelsefångst
Till exempel, i vårt fall, när du klickar på knappelementet, i händelsefångst, kommer händelseavlyssnarna på div-elementet att vara de första som utlöses. Lyssnarna på span-elementet följer efter och slutligen kommer lyssnarna på målelementet att triggas.
Händelsebubbling är dock standardsättet på vilket händelser sprids i Document Object Model (DOM). För att ändra standardbeteendet från händelsebubbling till händelsefångst, skickar vi ett tredje argument till våra händelseavlyssnare för att ställa in händelsefångst till sant. Om du inte skickar in ett tredje argument till händelseavlyssnarna, är händelsefångningen inställd på falsk.
Tänk på evenemangslyssnaren nedan:
div.addEventListener('click', () => { console.log("You've clicked a div element") })
Eftersom det inte har något tredje argument är fånga inställt på falskt. För att ställa in fånga till sant, skickar vi in ett tredje argument, det booleska värdet true, som ställer in fånga till sant.
div.addEventListener('click', () => { console.log("You've clicked a div element") }, true)
Alternativt kan du skicka in ett objekt som sätter infångning till sant, som visas nedan:
div.addEventListener('click', () => { console.log("You've clicked a div element") }, {capture: true})
För att testa händelsefångst, i din JavaScript-fil, lägg till ett tredje argument till alla dina händelselyssnare enligt bilden:
const div = document.querySelector('div'); div.addEventListener('click', () => { console.log("You've clicked a div element") }, true) const span = document.querySelector('span'); span.addEventListener('click', (e) => { console.log("You've clicked a span element") }, true) const button = document.querySelector('button'); button.addEventListener('click', () => { console.log("You've clicked a button"); }, true)
Öppna nu din webbläsare och klicka på knappen. Du bör få en sådan utdata:
Lägg märke till att till skillnad från händelsebubbling, där utdata från knappen skrevs ut först, vid händelsefångning, är den första utdata från det yttersta elementet, div.
Händelsebubbling och händelsefångst är de huvudsakliga sätten genom vilka händelser sprids i DOM. Men händelsebubbling är det som vanligtvis används för att sprida händelser.
Event Delegation
Händelsedelegering är ett designmönster där en enskild händelseavlyssnare är kopplad till ett gemensamt överordnat element, till exempel ett
- -element, istället för att ha händelseavlyssnare på vart och ett av de underordnade elementen. Händelser på underordnade element bubblar sedan upp till det överordnade elementet, där de hanteras av händelsehanteraren på det överordnade elementet.
Därför, i ett fall där vi har ett överordnat element med underordnade element inuti det, lägger vi bara till en händelseavlyssnare till det överordnade elementet. Denna händelsehanterare kommer att hantera alla händelser i de underordnade elementen.
Du kanske undrar hur det överordnade elementet kommer att veta vilket underordnat element som klickades på. Som nämnts tidigare skickar en händelseavlyssnare ett händelseobjekt till en händelsehanterare. Detta händelseobjekt har metoder och egenskaper som erbjuder information om en viss händelse. En av egenskaperna i händelseobjektet är målegenskapen. Målegenskapen pekar på det specifika HTML-elementet där en händelse inträffade.
Till exempel, om vi har en oordnad lista med listobjekt och vi kopplar en händelseavlyssnare till
- -elementet när en händelse inträffar på ett listobjekt, kommer målegenskapen i händelseobjektet att peka på det specifika listobjektet där händelsen inträffar inträffade.
För att se händelsedelegering i aktion, lägg till följande HTML-kod till den befintliga HTML-filen:
<ul> <li>Toyota</li> <li>Subaru</li> <li>Honda</li> <li>Hyundai</li> <li>Chevrolet</li> <li>Kia</li> </ul>
Lägg till följande JavaScript-kod för att använda händelsedelegering för att använda en enda händelseavlyssnare på ett överordnat element för att lyssna efter händelser i underordnade element:
const ul = document.querySelector('ul'); ul.addEventListener('click', (e) => { // target element targetElement = e.target // log out the content of the target element console.log(targetElement.textContent) })
Öppna nu webbläsaren och klicka på valfritt objekt i listan. Innehållet i elementet ska skrivas ut på konsolen enligt nedan:
Med en enda händelseavlyssnare kan vi hantera händelser i alla underordnade element. Att ha så många evenemangslyssnare på en sida påverkar dess prestanda, förbrukar mer minne och leder till långsam laddning och rendering av sidor.
Eventdelegering låter oss undvika allt detta genom att minimera antalet evenemangslyssnare vi behöver använda på en sida. Eventdelegering förlitar sig på händelsebubbling. Därför kan vi säga att händelsebubbling kan hjälpa till att optimera prestandan för webbsidor.
Tips för effektiv eventhantering
Som utvecklare, när du arbetar med händelser i dokumentobjektmodellen, överväg att använda händelsedelegering snarare än att ha många händelseavlyssnare på element på din sida.
När du använder händelsedelegering, kom ihåg att koppla en händelseavlyssnare till den närmaste gemensamma förfadern till de underordnade elementen som behöver händelsehantering. Detta hjälper till att optimera händelsebubbling och minimerar också vägen som en händelse måste gå innan den hanteras.
När du hanterar händelser, använd händelseobjektet som tillhandahålls av händelseavlyssnaren till din fördel. Händelseobjektet innehåller egenskaper som mål, som kommer väl till pass vid hantering av händelser.
Undvik överdriven DOM-manipulation för att ha mer presterande webbplatser. Att ha händelser som utlöser frekventa DOM-manipulationer kan påverka prestandan på din webbplats negativt.
Slutligen, i fallet med kapslade element, var mycket försiktig när du kopplar kapslade händelseavlyssnare till elementen. Detta kan inte bara påverka prestandan utan kommer att göra händelsehanteringen mycket komplex, och din kod kommer att vara svår att underhålla.
Slutsats
Händelser är ett kraftfullt verktyg i JavaScript. Händelsebubbling, händelsefångst och händelsedelegering är viktiga verktyg för att hantera händelser i JavaScript. Som webbutvecklare kan du använda artikeln för att bekanta dig med koncept så att du kan bygga mer interaktiva, dynamiska och prestandafulla webbplatser och applikationer.