Introduktion till popup-evenemang. Avancerat arbete med Event-objektet i JavaScript Avbryter event bubbling js

I den här lektionen kommer vi att bekanta oss med begreppet event bubbling, och även titta på hur det kan avbrytas. Dessutom kommer vi att ta reda på vilka andra stadier (faser) händelsen går igenom innan den börjar dyka upp.

Händelsebubbla

Om en händelse inträffar för något element börjar den "pop up", d.v.s. förekommer hos en förälder, sedan hos en morförälder osv.

Det följer att en händelse som genereras av något element kan fångas upp med hjälp av en hanterare på sin förälder, morförälder, etc.

Vi kommer att demonstrera uppkomsten av en händelse (bubbla) med hjälp av följande exempel:

Rubrik

En mycket viktig text

Kapitel

Lite text

Resten av texten

Låt oss skriva ett litet skript med vilket vi lägger till en "klick"-händelsehanterare för alla sidelement, såväl som för dokument- och fönsterobjekten.

document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); för (var i=0; i< allElements.length; i++) { allElements[i].addEventListener("click",function() {console.log(this.tagName);},false); }; document.addEventListener("click",function() {console.log(this);},false); window.addEventListener("click",function() {console.log(this);},false); });

Låt oss skapa en HTML-sida och klistra in ovanstående HTML-kod i den. Manus skrivet i JavaScript, infoga före den avslutande body-taggen. Efter detta, öppna den nyskapade sidan i en webbläsare, tryck på F12-tangenten och gå till konsolen. Låt oss nu vänsterklicka i området som hör till det starka elementet och se hur händelsen dyker upp.

Hur man avbryter händelsebubblingen

Uppkomsten av en händelse (bubbla) kan avbrytas. I det här fallet kommer denna händelse inte att utlösas för högre (överordnade) element. Metoden som är designad för att stoppa händelsen (bubblan) från att spridas kallas stopPropagation() .

Låt oss till exempel ändra vårt exempel ovan så att händelsen inte bubblar upp ovanför brödtexten: document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); för (var i=0 i

Utan tvekan är ytbeläggning mycket bekväm och arkitektoniskt transparent. Sluta inte med det om det inte är absolut nödvändigt.

Hämta elementet som anropade hanteraren

För att få DOM-elementet (objektet) som anropade händelsehanteraren måste du använda nyckeln ordet detta. Given nyckelord(detta) är endast tillgängligt i hanteraren om du prenumererar på händelsen med JavaScript.

Låt oss till exempel visa id:t för elementet som anropade händelsehanteraren i konsolen:

Var myP = document.getElementById("myP"); myP.addEventListener("klicka",function())( //hämta DOM-elementet som anropade händelsehanteraren - detta //få dess id och skicka det till konsolen console.log(this.id); ));

Du kan också använda egenskapen currentTarget (event.currentTarget) för att få det aktuella elementet.

Stadier (faser) av evenemanget

Innan en händelse börjar dyka upp (uppstigningsstadiet) går den först igenom ytterligare två steg:

  • Steg 1 är stadiet av nedsänkning till elementet som genererade händelsen. De där. i detta skede sker en rörelse från topp till botten, dvs. från fönsterobjekt till element. Detta stadium kallas även för avlyssningsstadiet.
  • Steg 2 är steget för att uppnå målet, d.v.s. element (objekt) som genererade händelsen.

Med hänsyn till alla stadier som ett evenemang går igenom erhålls följande bild:

Låt oss ändra ovanstående exempelskript enligt följande:

Document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); för (var i=0; i

Den tredje parametern i metoderna addEventListener och removeEventListener bestämmer i vilket skede händelsen kommer att fångas. Om denna parameterär inställd på sant kommer händelsen att avlyssnas vid händelsens nedsänkning (avlyssning). Och om parametern är falsk kommer händelsen att fångas upp i bubblingsstadiet. För att hantera händelsen på själva målet kan du använda addEventListener-metoden som med värde falskt, och med värdet true .

Observera: under nedsänkningsstadiet (avlyssning) kan händelser endast fångas upp av hanterare som lagts till med metoden addEventListener() . Hanterare har lagts till med andra metoder ( HTML-attribut eller via JavaScript med hjälp av egenskapen on[event]) kan fånga upp händelser endast i bubblingsstadiet.

Hämta elementet som genererade händelsen

För att få målelementet, dvs. elementet som genererade händelsen måste använda target-egenskapen (event.target).

Tänk på exemplet ovan, där vi ändrar innehållet i skriptelementet till följande:

Document.addEventListener("DOMContentLoaded", function() ( var elementBody = document.body; elementBody.addEventListener("click",function())( console.log(this.tagName + " - elementet som anropade hanteraren") ; console .log(event.currentTarget.tagName + " - elementet som anropade hanteraren" console.log(event.target.tagName + " - elementet som genererade händelsen" ),false);

Låt oss demonstrera vårt exempel genom att vänsterklicka i området som hör till det starka elementet:

Det hela började med användningen av JavaScript och klasser.

Jag har dock ett problem. Jag ville använda något som heter Bubble Events, men jag ville också minimera de beroenden som jag skulle behöva injicera. Jag ville inte ansluta jQuery-bibliotek för "det här lilla testet", bara för att använda popup-händelser.

Låt oss ta en närmare titt på vad rostningsevenemang är, hur de fungerar och några sätt att implementera dem.

Okej, så vad är problemet? Låt oss titta på ett enkelt exempel:

Låt oss säga att vi har en lista med knappar. Varje gång jag klickar på en av dem ska den bli "aktiv". Efter att ha tryckt igen bör knappen återgå till sitt ursprungliga tillstånd.

Låt oss börja med HTML:en:

  • Penna
  • Penna
  • suddgummi

Jag skulle kunna använda en standard JavaScript-händelsehanterare så här:

For(var i = 0; i< buttons.length; i++) { var button = buttons[i]; button.addEventListener("click", function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }); }
Ser bra ut... Men det kommer inte att fungera. Förbi minst, inte som vi förväntar oss det.

Stängningar vinner För den som kan lite funktionell JavaScript är problemet uppenbart.

För övrigt ska jag kort förklara - hanterarfunktionen är låst till knappvariabeln. Detta är dock en enda variabel och skrivs över varje iteration.

I den första iterationen hänvisar variabeln till den första knappen. I nästa - till den andra och så vidare. Men när användaren klickar på knappen har loopen redan avslutats och knappvariabeln refererar till den sista knappen, som alltid anropar händelsehanteraren för det. Oordning.

Vad vi behöver är ett separat sammanhang för varje funktion:

Var buttons = document.querySelectorAll(".verktygsfältsknapp"); var createToolbarButtonHandler = function(button) (retur funktion() (if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); ); för(var i = 0; i< buttons.length; i++) { buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i])); }
Mycket bättre! Och viktigast av allt, det fungerar korrekt. Vi har skapat en funktion createToolbarButtonHandle som returnerar en händelsehanterare. Sedan bifogar vi en egen hanterare för varje knapp.

Så vad är problemet? Det ser bra ut och fungerar. Trots detta kan vi fortfarande göra vår kod bättre.

För det första skapar vi för många hanterare. För varje knapp i .verktygsfältet skapar vi en funktion och binder den som en händelsehanterare. För tre knappar är minnesanvändningen försumbar.

Men om vi har något sånt här:

  • Foo
  • Bar
  • // ...ytterligare 997 element...
  • baz

då kommer datorn naturligtvis inte att explodera av spill. Vår minnesanvändning är dock långt ifrån idealisk. Vi tilldelar en enorm mängd av det, även om vi kan klara oss utan det. Låt oss skriva om vår kod igen så att vi kan använda samma funktion flera gånger.

Istället för att hänvisa till knappvariabeln för att hålla reda på vilken knapp vi klickade på, kan vi använda ett händelseobjekt, som skickas som det första argumentet till varje händelsehanterare.

Event-objektet innehåller en del data om händelsen. I vårt fall är vi intresserade av fältet currentTarget. Från den får vi en länk till elementet som klickades på:

Var toolbarButtonHandler = function(e) ( var button = e.currentTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" ); för(var i = 0; i< buttons.length; i++) { button.addEventListener("click", toolbarButtonHandler); }
Bra! Vi har inte bara förenklat allt till en enda funktion som används flera gånger, vi har också gjort vår kod mer läsbar genom att ta bort en onödig generatorfunktion.

Men vi kan fortfarande göra bättre.

Låt oss säga att vi lade till några knappar på arket efter att vår kod hade körts. Då skulle vi också behöva lägga till händelsehanterare för var och en av dem. Och vi skulle behöva lagra en länk till den här hanteraren och länkar från andra platser. Ser inte alltför lockande ut.

Kanske finns det ett annat tillvägagångssätt?

Låt oss börja med att förstå hur händelser fungerar och hur de rör sig längs vår DOM.

Hur fungerar de flesta av dem? När användaren klickar på ett element genereras en händelse för att meddela applikationen om detta. Resan för varje händelse sker i tre steg:
  • Avlyssningsfas
  • Händelse inträffar på målelementet
  • Uppstigningsfas
  • Notera: inte alla händelser går igenom avlyssnings- eller bubblingsstadiet. Vissa skapas direkt på elementet. Detta är dock snarare ett undantag från regeln.

    Händelsen skapas utanför dokumentet och flyttas sedan sekventiellt genom DOM-hierarkin till målelementet. När den har nått sitt mål, hämtas händelsen från DOM-elementet på samma sätt.

    Här är vår HTML-mall:

    • Knapp A
    • Knapp B
    • Knapp C

    När användaren klickar på knapp A går händelsen så här:

    Start
    | #dokumentera
    | Avlyssningsfas
    | HTML
    | KROPP
    | UL
    | LI#li_1
    | Knapp A< - Событие возникает для целевого элемента
    | Uppstigningsfas
    | LI#li_1
    | UL
    | KROPP
    | HTML
    v #dokument

    Lägg märke till att vi kan spåra vägen en händelse tar för att nå sitt målelement. I vårt fall, för varje knapp som trycks ned, kan vi vara säkra på att händelsen kommer att bubbla upp igen och passerar genom dess förälder - ul-elementet. Vi kan använda detta och implementera popup-händelser.

    Bubbelhändelser Bubbelhändelser är de händelser som är kopplade till ett överordnat element, men exekveras endast om de uppfyller något villkor.

    Låt oss ta vårt verktygsfält som ett konkret exempel:

    Ul class="verktygsfält">

  • Penna
  • Penna
  • suddgummi

  • Nu när vi vet att varje klick på knappen kommer att dyka upp genom elementet ul.toolbar, låt oss bifoga vår händelsehanterare till det. Lyckligtvis har vi det redan:

    Var verktygsfält = document.querySelector(".verktygsfält"); toolbar.addEventListener("klick", function(e) ( var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList. remove("aktiv" ));
    Vi har nu mycket renare kod, och vi har till och med blivit av med slingor! Observera dock att vi har ersatt e.currentTarget med e.target . Anledningen ligger i att vi bearbetar händelser på en annan nivå.

    e.target är det faktiska målet för händelsen, där den tar sig igenom DOM, och varifrån den sedan kommer att bubbla upp.
    e.currentTarget - det aktuella elementet som hanterar händelsen. I vårt fall är detta ul.toolbar.

    Förbättrade Bubble Events det här ögonblicket Vi hanterar alla klick på varje element som dyker upp via ul.toolbar , men vårt valideringsvillkor är för enkelt. Vad skulle hända om vi hade en mer komplex DOM som innehöll ikoner och element som inte var designade för att klickas på?

    • Penna
    • Penna
    • suddgummi

    hoppsan! När vi nu klickar på li.separatorn eller ikonen lägger vi till klassen .active till den. Det här är åtminstone inte bra. Vi behöver ett sätt att filtrera händelser så att vi reagerar på det element vi behöver.

    Låt oss skapa en liten hjälpfunktion för detta:

    Var delegate = function(criteria, listener) ( return function(e) ( var el = e.target; do ( if (!criteria(el)) continue; e.delegateTarget = el; listener.apply(this, arguments); return; ) while((el = el.parentNode));
    Vår assistent gör två saker. Först kommer den att iterera över varje element och dess föräldrar och kontrollera om de uppfyller villkoret i parameterparametern. Om elementet uppfyller, lägger hjälparen till ett fält till händelseobjektet som heter delegateTarget, som lagrar elementet som uppfyller våra villkor. Och ringer sedan föraren. Följaktligen, om inget element uppfyller villkoret, kommer ingen hanterare att anropas.

    Vi kan använda det så här:

    Var verktygsfält = document.querySelector(".verktygsfält"); var buttonsFilter = function(elem) (retur elem.classList && elem.classList.contains("btn"); ); var buttonHandler = function(e) ( var button = e.delegateTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" ); toolbar.addEventListener("klick", delegate(buttonsFilter, buttonHandler));
    Precis vad läkaren beordrade: en händelsehanterare kopplad till ett element som gör allt arbete. Men det gör det bara för de element vi behöver. Och den svarar perfekt på att lägga till och ta bort objekt från DOM.

    Sammanfattning Vi gick kortfattat igenom grunderna för att implementera delegering (hantering av popup-händelser). rent JavaScript. Detta är bra eftersom vi inte behöver generera och bifoga ett gäng hanterare för varje element.

    Om jag ville göra ett bibliotek av detta eller använda koden i utvecklingen skulle jag lägga till ett par saker:

    En hjälpfunktion för att kontrollera om ett objekt uppfyller kriterierna i en mer enhetlig och funktionell form. Tycka om:

    Var criteria = ( isElement: function(e) ( return e instansof HTMLElement; ), hasClass: function(cls) ( return function(e) ( return criteria.isElement(e) && e.classList.contains(cls); ) ) //Fler kriterier);
    Delvis användning av assistenten skulle också vara användbart:

    Var partialDelgate = funktion(kriterier) ( return funktion(hanterare) ( return delgate(kriterier, hanterare); ) );
    Originalartikel: Understanding Delegated JavaScript Events
    (Från översättaren: min första, döm strikt.)

    Glad kodning!

    Händelser är handlingar eller händelser som händer i systemet du programmerar, som systemet berättar om så att du kan svara på dem på något sätt om så önskas. Om användaren till exempel klickar på en knapp på en webbsida kanske du vill svara på den åtgärden genom att visa en informationsruta. I den här artikeln diskuterar vi några viktiga begrepp kring händelser och tittar på hur de fungerar i webbläsare. Detta kommer inte att vara en uttömmande studie; du behöver att veta i detta skede.

    Förutsättningar: Mål:
    Grundläggande datorvana, grundläggande förståelse för HTML och CSS, JavaScript första steg.
    Att förstå den grundläggande teorin om händelser, hur de fungerar i webbläsare och hur händelser kan skilja sig åt i olika programmeringsmiljöer.
    En rad lyckliga händelser

    Som nämnts ovan är händelser handlingar eller händelser som händer i systemet du programmerar - systemet producerar (eller "avfyrar") en signal av något slag när en händelse inträffar, och tillhandahåller också en mekanism genom vilken någon form av åtgärd kan utföras tas automatiskt (det vill säga att en del kod körs) när händelsen inträffar. Till exempel på en flygplats när landningsbanan är fri för ett plan att lyfta, kommuniceras en signal till piloten och som ett resultat börjar de styra planet.

    När det gäller webben utlöses händelser i webbläsarfönstret och tenderar att vara kopplade till ett specifikt objekt som finns i det - det här kan vara ett enstaka element, en uppsättning element, HTML-dokumentet som laddas i den aktuella fliken, eller hela webbläsarfönstret. Det finns många olika typer av händelser som kan inträffa, till exempel:

    • Användaren klickar med musen över ett visst element eller håller markören över ett visst element.
    • Användaren trycker på en tangent på tangentbordet.
    • Användaren ändrar storlek på eller stänger webbläsarfönstret.
    • Ett formulär skickas in.
    • En video spelas upp, pausas eller avslutas.
    • Ett fel uppstår.

    Av detta (och genom att titta på MDN Event-referensen) kan du se att det finns många händelser som kan besvaras.

    Varje tillgänglig händelse har en händelsehanterare , vilket är ett kodblock (vanligtvis en JavaScript-funktion som du som programmerare skapar) som kommer att köras när händelsen utlöses. När ett sådant kodblock definieras för att köras som svar på en händelseutlösning, säger vi att vi registrerar en händelsehanterare. Observera att händelsehanterare ibland kallas händelseavlyssnare - de är i stort sett utbytbara för våra syften, även om de strängt taget arbetar tillsammans. Lyssnaren lyssnar efter händelsen som händer, och hanteraren är koden som körs som svar på att det händer.

    Obs: Webbhändelser är inte en del av det grundläggande JavaScript-språket - de definieras som en del av API:erna inbyggda i webbläsaren.

    Ett enkelt exempel

    Låt oss titta på ett enkelt exempel för att förklara vad vi menar här. Du har redan sett händelser och händelsehanterare i många av exemplen i den här kursen, men låt oss sammanfatta vår kunskap. I följande exempel har vi en single , som när den trycks in gör att bakgrunden ändras till en slumpmässig färg:

    Ändra färg

    Knapp (marginal: 10px);

    JavaScriptet ser ut så här:

    Const btn = document.querySelector("knapp"); function random(number) ( return Math.floor(Math.random() * (number+1)); ) btn.onclick = function() ( const rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; document.body.style.backgroundColor = rndCol )

    I den här koden lagrar vi en referens till knappen inuti en konstant som kallas btn , med hjälp av funktionen Document.querySelector(). Vi definierar också en funktion som returnerar ett slumptal. Den tredje delen av koden är händelsehanteraren. Btn-konstanten pekar på ett element, och den här typen av objekt har ett antal händelser som kan aktiveras, och därför är händelsehanterare tillgängliga. Vi lyssnar efter klickhändelsen som utlöses, genom att ställa in onclick-händelsehanterarens egenskap att vara lika med en anonym funktion som innehåller kod som genererar en slumpmässig RGB-färg och ställer in bakgrundsfärgen lika med den.

    Den här koden körs när klickhändelsen aktiveras på elementet, det vill säga när en användare klickar på det.

    Exemplet är som följer:

    Det är inte bara webbsidor

    En annan sak som är värd att nämna vid det här laget är att händelser inte är unika för JavaScript - de flesta programmeringsspråk har någon form av händelsemodell, och hur modellen fungerar skiljer sig ofta från JavaScripts sätt. Faktum är att händelsemodellen i JavaScript för webbsidor skiljer sig från händelsemodellen för JavaScript eftersom den används i andra miljöer.

    Inline händelsehanterare – använd inte dessa

    Du kanske också ser ett mönster som detta i din kod:

    Tryck på mig-funktionen bgChange() ( const rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; document.body.style.backgroundColor = rndCol)

    Den tidigaste metoden för att registrera händelsehanterare som finns på webben involverade HTML-attribut för händelsehanterare (eller inline-händelsehanterare ) som den som visas ovan - attributvärdet är bokstavligen JavaScript-koden du vill köra när händelsen inträffar. Exemplet ovan anropar en funktion definierad inuti ett element på samma sida, men du kan också infoga JavaScript direkt i attributet, till exempel:

    Tryck på mig

    Du kan hitta HTML-attributekvivalenter för många av händelsehanterarens egenskaper; du bör dock inte använda dessa - de anses vara dålig praxis. Det kan tyckas lätt att använda ett händelsehanterare-attribut om du bara gör något riktigt snabbt, men de blir mycket snabbt ohanterliga och ineffektiva.

    Till att börja med är det ingen bra idé att blanda ihop din HTML och din JavaScript, eftersom det blir svårt att analysera - det är bättre att ha JavaScript på ett ställe; om det finns i en separat fil kan du tillämpa det på flera HTML-dokument.

    Inte ens i en enda fil är inline-händelsehanterare en bra idé. En knapp är OK, men vad händer om du hade 100 knappar? Du måste lägga till 100 attribut till filen; det skulle mycket snabbt förvandlas till ett underhåll. Med JavaScript kan du enkelt mardröm lägga till en händelsehanterarfunktion till alla knappar på sidan oavsett hur många det var, med något som detta:

    Const buttons = document.querySelectorAll("knapp"); för (låt i = 0; i< buttons.length; i++) { buttons[i].onclick = bgChange; } buttons.forEach(function(button) { button.onclick = bgChange; });

    Obs: Att separera din programmeringslogik från ditt innehåll gör också din webbplats mer användarvänlig för sökmotorer.

    addEventListener() och removeEventListener()

    Den senaste typen av händelsemekanism är definierad i Document Object Model (DOM) Level 2 Events Specification, som förser webbläsare med en ny funktion - addEventListener() . Detta fungerar på ett liknande sätt som händelsehanterarens egenskaper, men syntaxen är uppenbarligen annorlunda. Vi skulle kunna skriva om vårt slumpmässiga färgexempel så att det ser ut så här:

    Const btn = document.querySelector("knapp"); funktion bgChange() ( const rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; document.body.style.backgroundColor = rndCol; ) btn.addEventListener("klick", bgChange);

    Inuti addEventListener()-funktionen anger vi två parametrar - namnet på händelsen vi vill registrera den här hanteraren för och koden som består av hanterarfunktionen vi vill köra som svar på den. Observera att det är helt lämpligt att lägga all kod i funktionen addEventListener() i en anonym funktion, så här:

    Btn.addEventListener("klick", function() ( var rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; document.body .style.backgroundColor = rndCol);

    Denna mekanism har vissa fördelar jämfört med de äldre mekanismerna som diskuterats tidigare. Till att börja med finns det en motsvarighetsfunktion, removeEventListener() , som tar bort en tidigare tillagd lyssnare. Detta skulle till exempel ta bort lyssnaruppsättningen i det första kodblocket i det här avsnittet:

    Btn.removeEventListener("klick", bgChange);

    Detta är inte viktigt för enkla, små program, men för större, mer komplexa program kan det förbättra effektiviteten att rensa upp gamla oanvända händelsehanterare. Plus, till exempel, låter detta dig ha samma knapp som utför olika åtgärder under olika omständigheter. allt du behöver göra är att lägga till eller ta bort händelsehanterare efter behov.

    För det andra kan du också registrera flera hanterare för samma lyssnare. Följande två hanterare skulle inte båda tillämpas:

    MyElement.onclick = funktionA; myElement.onclick = funktionB;

    Den andra raden skriver över värdet för onclick som anges av den första raden. Detta skulle dock fungera:

    MyElement.addEventListener("klick", funktionA); myElement.addEventListener("klick", funktionB);

    Båda funktionerna körs nu när elementet klickas.

    Dessutom finns det ett antal andra kraftfulla funktioner och alternativ tillgängliga med denna händelsemekanism. Dessa är lite utanför räckvidden för den här artikeln, men om du vill läsa mer om dem, ta en titt på referenssidorna addEventListener() och removeEventListener().

    Vilken mekanism ska jag använda?

    Av de tre mekanismerna bör du definitivt inte använda HTML-händelsehanterarens attribut - dessa är föråldrade och dålig praxis, som nämnts ovan.

    De andra två är relativt utbytbara, åtminstone för enkla användningsområden:

    • Händelsehanterarens egenskaper har mindre kraft och alternativ, men bättre kompatibilitet över webbläsare (stöds så långt tillbaka som Internet Explorer 8). Du bör nog börja med dessa medan du lär dig.
    • DOM Level 2-händelser (addEventListener() , etc.) är mer kraftfulla, men kan också bli mer komplexa och stöds mindre väl (stödda så långt tillbaka som Internet Explorer 9). Du bör också experimentera med dessa, och sträva efter att använda dem där det är möjligt.

    De största fördelarna med den tredje mekanismen är att du kan ta bort händelsehanterarkod om det behövs, med removeEventListener() , och du kan lägga till flera lyssnare av samma typ till element om det behövs. Till exempel kan du anropa addEventListener("klick", function() ( ... )) på ett element flera gånger, med olika funktioner specificerade i det andra argumentet. Detta är omöjligt med händelsehanterares egenskaper eftersom alla efterföljande försök att ställa in en egenskap kommer att skriva över tidigare, t.ex.:

    Element.onclick = funktion1; element.onclick = funktion2; etc.

    Obs: Om du uppmanas att stödja webbläsare som är äldre än Internet Explorer 8 i ditt arbete, kan du stöta på problem, eftersom sådana gamla webbläsare använder andra händelsemodeller från nyare webbläsare. Men frukta aldrig, de flesta JavaScript-bibliotek (till exempel jQuery) har inbyggda funktioner som abstraherar bort skillnader över webbläsare. Oroa dig inte för mycket om detta i det här skedet av din inlärningsresa.

    Andra evenemangskoncept

    I det här avsnittet tar vi kort upp några avancerade koncept som är relevanta för evenemang. Det är inte viktigt att förstå dessa begrepp till fullo vid denna tidpunkt, men de kan tjäna till att förklara några kodmönster som du förmodligen kommer att stöta på då och då.

    Händelseobjekt

    Ibland i en händelsehanterarfunktion kan du se en parameter specificerad med ett namn som event , evt , eller helt enkelt e . Detta kallas händelseobjektet , och det skickas automatiskt till händelsehanterare för att tillhandahålla extra funktioner och information. Låt oss till exempel skriva om vårt slumpmässiga färgexempel igen något:

    Funktion bgChange(e) ( const rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; e.target.style.backgroundColor = rndColor ; console.log(e); btn.addEventListener("klick", bgChange);

    Här kan du se att vi inkluderar ett händelseobjekt, e , i funktionen, och i funktionsinställningen en bakgrundsfärgstil på e.target - vilket är själva knappen. Målegenskapen för händelseobjektet är alltid en referens till det element som händelsen just inträffade vid. Så i det här exemplet ställer vi in ​​en slumpmässig bakgrundsfärg på knappen, inte på sidan.

    Obs: Du kan använda vilket namn du vill för händelseobjektet - du behöver bara välja ett namn som du sedan kan använda för att referera till det i händelsehanterarfunktionen. e/evt/event används oftast av utvecklare eftersom de är korta och lätta att komma ihåg. Det är alltid bra att vara konsekvent - med sig själv och med andra om möjligt.

    e.target är otroligt användbart när du vill ställa in samma händelsehanterare på flera element och göra något med dem alla när en händelse inträffar på dem. Du kan till exempel ha en uppsättning med 16 brickor som försvinner när de klickas på. Det är användbart att alltid kunna ställa in saken så att den försvinner som e.target , istället för att behöva välja den på något svårare sätt. I följande exempel (se användbart-eventtarget.html för den fullständiga källkoden; se även att den körs live här), skapar vi 16 element med hjälp av JavaScript. Vi väljer sedan alla med hjälp av document.querySelectorAll() , går sedan igenom var och en och lägger till en onclick-hanterare till var och en som gör det så att en slumpmässig färg appliceras på var och en när den klickas:

    Const divs = document.querySelectorAll("div"); för (låt i = 0; i< divs.length; i++) { divs[i].onclick = function(e) { e.target.style.backgroundColor = bgChange(); } }

    Utgången är som följer (försök att klicka runt på den - ha kul):

    Dolt exempel Användbart händelsemålexempel div (höjd: 100px; bredd: 25%; flytande: vänster; ) för (låt i = 1; i
  • Penna
  • Penna
  • suddgummi

  • Nu när vi vet att varje klick på knappen kommer att dyka upp genom elementet ul.toolbar, låt oss bifoga vår händelsehanterare till det. Lyckligtvis har vi det redan:

    Var verktygsfält = document.querySelector(".verktygsfält"); toolbar.addEventListener("klick", function(e) ( var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList. remove("aktiv" ));
    Vi har nu mycket renare kod, och vi har till och med blivit av med slingor! Observera dock att vi har ersatt e.currentTarget med e.target . Anledningen ligger i att vi bearbetar händelser på en annan nivå.

    e.target är det faktiska målet för händelsen, där den tar sig igenom DOM, och varifrån den sedan kommer att bubbla upp.
    e.currentTarget - det aktuella elementet som hanterar händelsen. I vårt fall är detta ul.toolbar.

    Förbättrade popup-händelser För närvarande hanterar vi alla klick på varje element som dyker upp via ul.toolbar , men vårt valideringsvillkor är för enkelt. Vad skulle hända om vi hade en mer komplex DOM som innehöll ikoner och element som inte var designade för att klickas på?

    • Penna
    • Penna
    • suddgummi

    hoppsan! När vi nu klickar på li.separatorn eller ikonen lägger vi till klassen .active till den. Det här är åtminstone inte bra. Vi behöver ett sätt att filtrera händelser så att vi reagerar på det element vi behöver.

    Låt oss skapa en liten hjälpfunktion för detta:

    Var delegate = function(criteria, listener) ( return function(e) ( var el = e.target; do ( if (!criteria(el)) continue; e.delegateTarget = el; listener.apply(this, arguments); return; ) while((el = el.parentNode));
    Vår assistent gör två saker. Först kommer den att iterera över varje element och dess föräldrar och kontrollera om de uppfyller villkoret i parameterparametern. Om elementet uppfyller, lägger hjälparen till ett fält till händelseobjektet som heter delegateTarget, som lagrar elementet som uppfyller våra villkor. Och ringer sedan föraren. Följaktligen, om inget element uppfyller villkoret, kommer ingen hanterare att anropas.

    Vi kan använda det så här:

    Var verktygsfält = document.querySelector(".verktygsfält"); var buttonsFilter = function(elem) (retur elem.classList && elem.classList.contains("btn"); ); var buttonHandler = function(e) ( var button = e.delegateTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" ); toolbar.addEventListener("klick", delegate(buttonsFilter, buttonHandler));
    Precis vad läkaren beordrade: en händelsehanterare kopplad till ett element som gör allt arbete. Men det gör det bara för de element vi behöver. Och den svarar perfekt på att lägga till och ta bort objekt från DOM.

    Sammanfattning Vi tittade kort på grunderna för att implementera delegering (hantering av popup-händelser) i ren JavaScript. Detta är bra eftersom vi inte behöver generera och bifoga ett gäng hanterare för varje element.

    Om jag ville göra ett bibliotek av detta eller använda koden i utvecklingen skulle jag lägga till ett par saker:

    En hjälpfunktion för att kontrollera om ett objekt uppfyller kriterierna i en mer enhetlig och funktionell form. Tycka om:

    Var criteria = ( isElement: function(e) ( return e instansof HTMLElement; ), hasClass: function(cls) ( return function(e) ( return criteria.isElement(e) && e.classList.contains(cls); ) ) //Fler kriterier);
    Delvis användning av assistenten skulle också vara användbart:

    Var partialDelgate = funktion(kriterier) ( return funktion(hanterare) ( return delgate(kriterier, hanterare); ) );
    Originalartikel: Understanding Delegated JavaScript Events
    (Från översättaren: min första, döm strikt.)

    Glad kodning!

    Hallå! I den här lektionen vill jag prata om ett så viktigt begrepp som uppkomst och avlyssning av händelser. Bubbling är ett fenomen där om du klickar på ett underordnat element sprids händelsen till dess förälder.

    Det kan vara mycket användbart när du bearbetar stora kapslade listor eller tabeller, för att inte tilldela en händelsehanterare till varje element kan du tilldela en hanterare till det överordnade elementet, och händelsen kommer redan att spridas till alla kapslade element i det överordnade elementet. Låt oss titta på ett exempel.

    Den här hanteraren aktiveras om du klickar på en undertagg eller:

    Klicka på EM, hanteraren på DIV kommer att fungera

    Som du kan se, när du klickar på ett kapslat em-element, utlöses hanteraren på div. Varför händer det här? Läs vidare och ta reda på det.

    Uppstigning

    Så grundprincipen för uppstigning:

    När det finns en händelse av något slag spelar det ingen roll om du klickar med musen på ett element, händelsen kommer först att utlösas på det överordnade elementet och sedan längs kedjan sprids det till alla kapslade element.

    Anta till exempel att det finns tre kapslade element FORM > DIV > P, med en händelsehanterare på varje:

    body * (marginal: 10px; kant: 1px fast blå; ) FORM DIV

    Bubbling säkerställer att klicka på det inre elementet

    Kommer att ringa klickhanteraren (om det finns en såklart) först på själva

    Denna process kallas uppstigning, eftersom händelser verkar "flyta upp" från det inre elementet uppåt genom sina föräldrar, precis som en luftbubbla flyter upp i vatten, så du kan också hitta definitionen av bubblande, ja, det är bara från engelskan ordet bubblar - att flyta upp.

    Åtkomst till målelementet event.target

    För att ta reda på vilket element vi fångade den eller den händelsen finns metoden event.target. (läs om händelseobjektet).

    • event.target är det faktiska källelementet på vilket händelsen inträffade.
    • detta är alltid det aktuella elementet som bubblingen har nått, och hanteraren körs för närvarande på det.

    Till exempel, om du bara har en form.onclick-hanterare installerad, kommer den att "fånga" alla klick i formuläret. Dessutom, oavsett var klicket är inuti, kommer det fortfarande att dyka upp till elementet som hanteraren kommer att arbeta på.

    Vart i:

    • detta (=event.currentTarget) kommer alltid att vara själva formuläret, eftersom hanteraren triggades på den.
    • event.target kommer att innehålla en länk till ett specifikt element i formuläret, det mest kapslade där klicket inträffade.

    I princip kan detta sammanfalla med event.target om formuläret klickas och det inte finns fler element i formuläret.

    Stoppar uppstigningen

    Vanligtvis går händelsebubblingen direkt till toppen och når rotfönsterobjektet.

    Men det går att stoppa uppstigningen vid något mellanliggande moment.

    För att stoppa spridningen måste du anropa metoden event.stopPropagation().

    Låt oss titta på ett exempel: när en knapp klickas, fungerar inte body.onclick-hanteraren:

    Klicka här

    Om ett element har flera hanterare installerade för samma händelse, kommer alla att exekveras även om bubblingen upphör.

    Således kommer stopPropagation att förhindra händelsen från att spridas vidare, men alla hanterare kommer att arbeta på elementet, men inte på nästa element.

    För att stoppa bearbetningen av det aktuella elementet stöder webbläsare metoden event.stopImmediatePropagation(). Denna metod kommer inte bara att förhindra bubbling, utan kommer också att stoppa händelsebearbetning på det aktuella elementet.

    Dyka

    I standarden, förutom "uppstigningen" av händelser, finns det också ett "dyk".

    Dykning, till skillnad från uppstigning, är mindre efterfrågad, men det kommer fortfarande att vara användbart att veta om det.

    Så det finns tre stadier av evenemanget:

  • Händelsen kommer från topp till botten. Detta stadium kallas "avlyssningsstadiet".
  • Händelsen nådde ett specifikt moment. Detta är "målstadiet".
  • Efter allt börjar händelsen dyka upp. Detta är "uppstigningsstadiet".
  • Detta visas i standarden enligt följande:

    Således, när du klickar på TD:n, kommer händelsen att färdas längs kedjan av föräldrar, först ner till elementet ("sjunker") och sedan upp ("poppar upp"), med hjälp av hanterare längs vägen.

    Ovan skrev jag bara om uppstigning, eftersom de andra etapperna inte används och passerar obemärkta för oss.

    Hanterarna vet inget om avlyssningsstadiet utan börjar arbeta från uppstigningen.

    Och för att fånga en händelse i avlyssningsstadiet behöver du bara använda:

    • Argumentet är sant, då kommer händelsen att avlyssnas på vägen ner.
    • Argumentet är falskt, då kommer händelsen att fångas när den bubblar.
    Exempel

    I exemplet på , ,

    Processorerna är desamma som tidigare, men den här gången på nedsänkningsstadiet. Tja, för att se avlyssning i aktion, klicka på elementet i det

    Hanterarna kommer att arbeta i top-down-ordning: FORM → DIV → P.

    JS-koden här är:

    Var elems = document.querySelectorAll("form,div,p"); // koppla en hanterare till varje element vid avlyssningsstadiet för (var i = 0; i< elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); }


    Ingen hindrar dig från att tilldela hanterare för båda stegen, så här:

    Var elems = document.querySelectorAll("form,div,p"); för (var i = 0; i< elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); elems[i].addEventListener("click", highlightThis, false); }

    Klicka på det inre elementet

    För att se händelseordningen:
    Det ska vara FORM → DIV → P → P → DIV → FORM. Observera att elementet

    Kommer att delta i båda stegen.

    Resultat
    • När en händelse inträffar markeras elementet på vilket händelsen inträffade som event.target.
    • Händelsen flyttas först ner från dokumentroten till event.target och anropar hanterare längs vägen, levererad via addEventListener(…., true).
    • Händelsen flyttas från event.target upp till början av dokumentet, längs vägen anropar den hanterare som tillhandahålls via addEventListener(…., false).

    Varje hanterare kommer att ha tillgång till händelseegenskaperna:

    • event.target är det djupaste elementet där händelsen faktiskt inträffade.
    • event.currentTarget (=detta) – elementet på vilket självhanteraren för närvarande utlöses (till vilken händelsen har "nått").
    • event.eventPhase – vid vilken fas händelsehanteraren triggades (dyk = 1, ascend = 3).

    Förökning kan stoppas genom att anropa metoden event.stopPropagation(), men detta rekommenderas inte, eftersom du kan behöva händelsen för de mest oväntade syften.