Js event bubbling. JavaScript - Event Bubbling. How do most of them work?

💖 Do you like it? Share the link with your friends

Now we will look at some advanced things when working with the Event object, namely: bubbling and interception, as well as event delegation.

Bubbling Events

Imagine that you have several blocks nested inside each other:

innermost block

When you click on the innermost block, the onclick event fires first on it, and then fires on its parent, on its parent's parent, and so on, until it reaches the body tag and then the html tag (then document and window ).

And this is logical, because when you click on the internal block, you simultaneously click on all external ones.

Let's see this with the following example: we have 3 blocks, each of them has an onclick event attached:

Click on the innermost red block - and you will see how the onclick of the red block works first, then the blue one, then the green one:

This behavior is called by surfacing events - by analogy with the ascent of an air bubble from the bottom. Just like the bubble, our click on the inner element seems to float to the top, each time triggering on higher blocks.

event.target

Let us have two elements: a div and a paragraph p lying inside this div. Let us bind onlick to a div:

When we click on this div, we can go to a paragraph, or we can go to a place where this paragraph does not exist.

How this can be - look at the following example: green color is our div, and the blue one is our paragraph:

If we click on the green part, we will click on the div, and if we click on the blue part, the click will occur first on the paragraph, and then on the div. But since onclick is attached specifically to the div, we, in general, may not notice the presence of the paragraph.

However, sometimes we would like to know whether the click occurred directly on a div or on its descendant paragraph. The Event object and its event.target property will help us with this - it stores exactly the element in which the click occurred.

In the following example, we have a div with a p inside it and a span inside it.

Let's bind the onclick event ourselves top element(div) and we will click on different elements: div, p, span. Using event.target we will get the lowest element in which the event occurred and display its name using tagName .

If you click, for example, on a span, then the event will catch our div (after all, the onclick is attached to it), but the span will be in event.target:

Click on different blocks - you will see the result:

Stopping ascent

So, you already know that all events bubble up to the very top (to the html tag, and then to document, and then to window). Sometimes there is a need to stop this ascent. Any element through which the event bubbles can do this. To do this, call the event.stopPropagation() method in the element code.

In the following example, clicking on the red block will work on itself, then on the blue block and that’s it - the blue block stops further popping up and the green block will not react in any way:

Click on the red block - you will see the result:

Dive

In addition to the emergence of events, there is also dive(according to scientific interception stage). This means that the event first comes from top to bottom (interception stage), reaches our element (goal stage) and only then begins to emerge (ascent stage).

You can only attach an event handler taking into account the interception stage using addEventListener . For this, it has a third parameter: if it is true, the event will fire at the interception stage, and if false, at the bubbling stage (this is the default):

Var green = document.getElementById("green"); green.addEventListener("click", func, true); function func(event) ( )

The stage at which an event occurred can be determined using the event.eventPhase property. It can take the following values: 1 - interception stage, 2 - target stage, 3 - ascent stage.

Introduction to delegation

Let's imagine a situation: let us have a ul with several li . The following event is attached to each li: when you click on the li, “!” is added to the end of it.

Let's implement the described:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
var li = document.querySelectorAll("#ul li"); //In the loop we attach the addSign function to each li: for (var i = 0; i

Click on li - you will see how “!” is added to the end:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5

Let us now also have a button, when clicked, a new li with the text “item” is added to the end of ul. We're in for a surprise: the bound event won't work for new li's! Let's make sure of this:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
Add li

Click on the button to add li, and then on this new li - it will not react:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
Add li

To solve the problem, when creating a new li, you can attach the addSign function to it via addEventListener. Let's implement this:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
Add li var li = document.querySelectorAll("#ul li"); for (var i = 0; i

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
Add li

There is a second way to get around the problem - event delegation. Let's break it down.

Event delegation

The essence of delegation is as follows: let’s assign an event not to each li, but to their parent - on ul.

At the same time, the functionality of our script should remain: as before, when you click on li, “!” will be added to the end of it. Only the event in the new version will be attached to ul:

Var ul = document.getElementById("ul"); //Attach the event to ul: ul.addEventListener("click", addSign); function addSign() ( )

How we will check this: since the event is attached to ul, then inside the function we can catch li using event.target . Let me remind you what event.target is - this is exactly the tag in which the click occurred, in our case it is li .

So, here is the solution to our problem through delegation:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5

Result of executing the code:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5

Moreover, our solution will work automatically even for new li , because the event is assigned not to li, but to ul:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
Add li var ul = document.getElementById("ul"); ul.addEventListener("click", addSign); function addSign() ( event.target.innerHTML = event.target.innerHTML + "!"; ) //Implementation of a button to add a new li: var button = document.getElementById("button"); button.addEventListener("click", addLi); function addLi() ( var li = document.createElement("li"); li.innerHTML = "new li"; ul.appendChild(li); )

Click on the button to add li, and then on this new li - it will react:

  • paragraph 1
  • point 2
  • point 3
  • point 4
  • point 5
Add li

Our code is working, but not without its flaws. Let's look at these shortcomings and write a more universal solution.

Universal Event Delegation

The shortcoming of our code will manifest itself when there are some nested tags inside li. In our case, let these be the i tags:

In this case, pressing i will add exclamation point to the end of the i tag, and not the li tag, as we would like (if you click on li outside italics, then everything will be ok):

  • paragraph italics 1
  • paragraph italics 2
  • paragraph italics 3
  • paragraph italics 4
  • paragraph italics 5
var ul = document.getElementById("ul"); ul.addEventListener("click", addSign); function addSign() ( event.target.innerHTML = event.target.innerHTML + "!"; )

Click on italics - you will see how "!" will be added to the end of it (pressing outside italics will work fine):

The problem is fixed as follows (the described method is not the only one, but the simplest): using the closest method, we find the closest li, which is the parent of event.target like this: event.target.closest("li") .

How it works: if the click was on i , then event.target contains this i, and event.target.closest("li") contains our li for which the event should fire.

If the click was on the li itself, then both event.target and event.target.closest("li") will contain our li.

Let's check:

  • paragraph italics 1
  • paragraph italics 2
  • paragraph italics 3
  • paragraph italics 4
  • paragraph italics 5
var ul = document.getElementById("ul"); ul.addEventListener("click", function(event) ( var li = event.target.closest("li"); if (li) ( //check if there is no li parent at all li.innerHTML = li.innerHTML + "!"; ) ));

Result of executing the code:

It doesn’t matter what the depth of nesting is: the i tag can lie in the b tag, and that in the span tag and only then in li - it doesn’t matter: the event.target.closest("li") construction will find a parent from any nesting level.

In this lesson, we will become familiar with the concept of event bubbling, and also look at how it can be interrupted. In addition, we will find out what other stages (phases) the event goes through before it starts to emerge.

Event Bubble

If an event occurs for some element, it begins to “pop up”, i.e. occurs in a parent, then in a grandparent, etc.

It follows that an event generated by some element can be intercepted using a handler on its parent, grandparent, etc.

We will demonstrate the emergence of an event (bubble) using the following example:

Heading

Some very important text

Chapter

Some text

Rest of text

Let's write a small script with which we will add a "click" event handler for all page elements, as well as for the document and window objects.

document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); for (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); });

Let's create an HTML page and paste the above HTML code into it. We will insert the script written in JavaScript before the closing body tag. After this, open the newly created page in a web browser, press the F12 key and go to the console. Now let's left-click in the area belonging to the strong element and see how the event pops up.

How to interrupt event bubbling

The rising of an event (bubble) can be interrupted. In this case, this event will not be triggered for higher (parent) elements. The method that is designed to stop an event (bubble) from propagating is called stopPropagation() .

For example, let's change our example above so that the event does not bubble up above the body: document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); for (var i=0; i

Undoubtedly, surfacing is very convenient and architecturally transparent. Don't stop it unless absolutely necessary.

Getting the element that called the handler

In order to get the DOM element (object) that called the event handler, you must use the this keyword. This keyword (this) is only available in the handler if you subscribe to the event using JavaScript.

For example, let's display in the console the id of the element that called the event handler:

Var myP = document.getElementById("myP"); myP.addEventListener("click",function())( //get the DOM element that called the event handler - this //get its id and output it to the console console.log(this.id); ));

You can also use the currentTarget property (event.currentTarget) to get the current element.

Stages (phases) of the event

Before an event begins to emerge (the ascent stage), it first goes through 2 more stages:

  • Stage 1 is the stage of immersion to the element that generated the event. Those. at this stage there is a movement from top to bottom, i.e. from the window object to the element. This stage is also called the interception stage.
  • Stage 2 is the stage of achieving the goal, i.e. element (object) that generated the event.

Taking into account all the stages that an event goes through, the following picture emerges:

Let's modify the above example script as follows:

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

The third parameter of the addEventListener and removeEventListener methods determines the stage at which the event will be caught. If this parameter is true, then the event will be intercepted at the event immersion (interception) stage. And if the parameter is false , then the event will be intercepted at the bubbling stage. To handle an event on the target itself, you can use the addEventListener method with either the false or true value.

Attention: during the immersion (interception) stage, events can only be intercepted by handlers added using the addEventListener() method. Handlers added via other methods (an HTML attribute or via JavaScript using the on[event] property) can only intercept events at the bubbling stage.

Getting the element that generated the event

In order to get the target element, i.e. the element that generated the event must use the target property (event.target).

Consider the example above, in which we change the content of the script element to the following:

Document.addEventListener("DOMContentLoaded", function() ( var elementBody = document.body; elementBody.addEventListener("click",function())( console.log(this.tagName + " - the element that called the handler"); console .log(event.currentTarget.tagName + " - the element that called the handler"); console.log(event.target.tagName + " - the element that generated the event"); ),false); ));

Let's demonstrate our example by left-clicking in the area belonging to the strong element:

Hello! In this lesson I want to talk about such an important concept as surfacing and interception of events. Bubbling is a phenomenon where if you click on a child element, the event propagates to its parent.

It can be very useful when processing large nested lists or tables; in order not to assign an event handler to each element, you can assign one handler to the parent element, and the event will already propagate to all nested elements in the parent. Let's look at an example.

This handler will fire if you click on a subtag or :

Click on EM, the handler on DIV will work

As you can see, when you click on a nested em element, the handler on the div is triggered. Why is this happening? Read on and find out.

Ascent

So the basic principle of ascent:

When there is an event of any kind, it doesn’t matter if the mouse is clicked on an element, the event will first fire on the parent element, and then along the chain it will propagate to all nested elements.

For example, suppose there are 3 nested elements FORM > DIV > P, with an event handler on each:

body * ( margin: 10px; border: 1px solid blue; ) FORM DIV

Bubbling ensures that clicking on the inner element

Will call the click handler (if it exists of course) first on the actual

This process is called bubbling, because events seem to “float up” from the internal element upward through their parents, just as an air bubble floats up in water, so you can also find the definition of bubbling, well, it’s just from English word bubbling - to float up.

Accessing the target element event.target

In order to find out on which element we caught this or that event, there is the event.target method. (read about the event object).

  • event.target is the actual source element on which the event occurred.
  • this is always the current element that the bubbling has reached, and the handler is currently running on it.

For example, if you have only one form.onclick handler installed, then it will “catch” all clicks inside the form. Moreover, no matter where the click is inside, it will still pop up to the element, on which the handler will work.

Wherein:

  • this (=event.currentTarget) will always be the form itself, since the handler was triggered on it.
  • event.target will contain a link to a specific element within the form, the most nested one on which the click occurred.

In principle, this can coincide with event.target if the form is clicked and there are no more elements in the form.

Stopping ascent

Typically, the event bubbling goes straight to the top and reaches the root window object.

But it is possible to stop the ascent at some intermediate element.

In order to stop propagation, you need to call the event.stopPropagation() method.

Let's look at an example: when a button is clicked, the body.onclick handler will not work:

Click me

If an element has several handlers installed for the same event, then even if bubbling stops, all of them will be executed.

Thus, stopPropagation will prevent the event from propagating further, but all handlers will work on the element, but not on the next element.

To stop processing on the current element, browsers support the event.stopImmediatePropagation() method. This method will not only prevent bubbling, but will also stop event processing on the current element.

Dive

In the standard, in addition to the “ascent” of events, there is also a “dive”.

Diving, unlike ascent, is less in demand, but it will still be useful to know about it.

So there are 3 stages of the event:

  • The event comes from top to bottom. This stage is called the "interception stage".
  • The event reached a specific element. This is the “goal stage”.
  • After everything, the event begins to emerge. This is the “ascent stage”.
  • This is demonstrated in the standard as follows:

    Thus, when you click on the TD, the event will travel along the chain of parents, first down to the element (“sinks”), and then up (“pops up”), using handlers accordingly along the way.

    Above I wrote only about ascent, because the other stages are not used and pass unnoticed by us.

    The handlers do not know anything about the interception stage, but start working from the ascent.

    And to catch an event at the interception stage, you just need to use:

    • The argument is true, then the event will be intercepted on the way down.
    • The argument is false, then the event will be caught when bubbling.
    Examples

    In the example at , ,

    The processors are the same as before, but this time at the immersion stage. Well, to see interception in action, click on the element in it

    The handlers will work in top-down order: FORM → DIV → P.

    The JS code here is:

    Var elems = document.querySelectorAll("form,div,p"); // attach a handler to each element at the interception stage for (var i = 0; i< elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); }


    No one is stopping you from assigning handlers for both stages, like this:

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

    Click on the inner element

    To see the order of events:
    It should be FORM → DIV → P → P → DIV → FORM. Note that the element

    Will participate in both stages.

    Results
    • When an event occurs, the element on which the event occurred is marked as event.target.
    • The event first moves down from the document root to event.target, calling handlers along the way, supplied via addEventListener(…., true).
    • The event moves from event.target up to the beginning of the document, along the way it calls handlers supplied via addEventListener(…., false).

    Each handler will have access to the event properties:

    • event.target is the deepest element where the event actually occurred.
    • event.currentTarget (=this) – the element on which this moment The self-processor (to which the event “reached”) was triggered.
    • event.eventPhase – at what phase the event handler was triggered (dive = 1, ascend = 3).

    Propagation can be stopped by calling the event.stopPropagation() method, but this is not recommended, since you may need the event for the most unexpected purposes.

    Events are actions or occurrences that happen in the system you are programming, which the system tells you about so you can respond to them in some way if desired. For example, if the user clicks a button on a webpage, you might want to respond to that action by displaying an information box. In this article, we discuss some important concepts surrounding events, and look at how they work in browsers. This won't be an exhaustive study; just what you need to know at this stage.

    Prerequisites: Objective:
    Basic computer literacy, a basic understanding of HTML and CSS, JavaScript first steps.
    To understand the fundamental theory of events, how they work in browsers, and how events may differ in different programming environments.
    A series of fortunate events

    As mentioned above, events are actions or occurrences that happen in the system you are programming - the system produces (or "fires") a signal of some kind when an event occurs, and also provides a mechanism by which some kind of action can be automatically taken (that is, some code running) when the event occurs. For example in an airport when the runway is clear for a plane to take off, a signal is communicated to the pilot, and as a result, they commence piloting the plane.

    In the case of the Web, events are fired inside the browser window, and tend to be attached to a specific item that resides in it - this might be a single element, set of elements, the HTML document loaded in the current tab, or the entire browser window. There are a lot of different types of events that can occur, for example:

    • The user clicking the mouse over a certain element or hovering the cursor over a certain element.
    • The user pressing a key on the keyboard.
    • The user resizing or closing the browser window.
    • A form being submitted.
    • A video being played, or paused, or finishing play.
    • An error occurring.

    You can gather from this (and from glancing at the MDN Event reference) that there are a lot of events that can be responded to.

    Each available event has an event handler , which is a block of code (usually a JavaScript function that you as a programmer create) that will be run when the event fires. When such a block of code is defined to be run in response to an event firing, we say we are registering an event handler . Note that event handlers are sometimes called event listeners - they are pretty much interchangeable for our purposes, although strictly speaking, they work together. The listener listens out for the event happening, and the handler is the code that is run in response to it happening.

    Note: Web events are not part of the core JavaScript language - they are defined as part of the APIs built into the browser.

    A simple example

    Let's look at a simple example to explain what we mean here. You've already seen events and event handlers used in many of the examples in this course already, but let's recap just to cement our knowledge. In the following example , we have a single , which when pressed, makes the background change to a random color:

    Change color

    Button ( margin: 10px );

    The JavaScript looks like so:

    Const btn = document.querySelector("button"); 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; )

    In this code, we store a reference to the button inside a constant called btn , using the Document.querySelector() function. We also define a function that returns a random number. The third part of the code is the event handler. The btn constant points to a element, and this type of object has a number of events that can fire on it, and therefore, event handlers available. We are listening for the click event firing, by setting the onclick event handler property to equal an anonymous function containing code that generates a random RGB color and sets the background-color equal to it.

    This code is run whenever the click event fires on the element, that is, whenever a user clicks on it.

    The example output is as follows:

    It"s not just web pages

    Another thing worth mentioning at this point is that events are not unique to JavaScript - most programming languages ​​have some kind of event model, and the way the model works often differs from JavaScript"s way. In fact, the event model in JavaScript for web pages differs from the event model for JavaScript as it is used in other environments.

    Inline event handlers - don't use these

    You might also see a pattern like this in your code:

    Press me function bgChange() ( const rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; document.body.style.backgroundColor = rndCol; )

    The earliest method of registering event handlers found on the Web involved event handler HTML attributes (or inline event handlers ) like the one shown above - the attribute value is literally the JavaScript code you want to run when the event occurs. The above example invokes a function defined inside a element on the same page, but you could also insert JavaScript directly inside the attribute, for example:

    Press me

    You can find HTML attribute equivalents for many of the event handler properties; however, you shouldn't use these - they are considered bad practice. It might seem easy to use an event handler attribute if you are just doing something really quick, but they very quickly become unmanageable and ineffective.

    For a start, it is not a good idea to mix up your HTML and your JavaScript, as it becomes hard to parse - keeping your JavaScript all in one place is better; if it is in a separate file you can apply it to multiple HTML documents.

    Even in a single file, inline event handlers are not a good idea. One button is OK, but what if you had 100 buttons? You"d have to add 100 attributes to the file; it would very quickly turn into a maintenance. With JavaScript, you could easily nightmare add an event handler function to all the buttons on the page no matter how many there were, using something like this:

    Const buttons = document.querySelectorAll("button"); for (let i = 0; i< buttons.length; i++) { buttons[i].onclick = bgChange; } buttons.forEach(function(button) { button.onclick = bgChange; });

    Note: Separating your programming logic from your content also makes your site more friendly to search engines.

    addEventListener() and removeEventListener()

    The newest type of event mechanism is defined in the Document Object Model (DOM) Level 2 Events Specification, which provides browsers with a new function - addEventListener() . This functions in a similar way to the event handler properties, but the syntax is obviously different. We could rewrite our random color example to look like this:

    Const btn = document.querySelector("button"); function bgChange() ( const rndCol = "rgb(" + random(255) + "," + random(255) + "," + random(255) + ")"; document.body.style.backgroundColor = rndCol; ) btn.addEventListener("click", bgChange);

    Inside the addEventListener() function, we specify two parameters - the name of the event we want to register this handler for, and the code that comprises the handler function we want to run in response to it. Note that it is perfectly appropriate to put all the code inside the addEventListener() function, in an anonymous function, like this:

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

    This mechanism has some advantages over the older mechanisms discussed earlier. For a start, there is a counterpart function, removeEventListener() , which removes a previously added listener. For example, this would remove the listener set in the first code block in this section:

    Btn.removeEventListener("click", bgChange);

    This isn't significant for simple, small programs, but for larger, more complex programs it can improve efficiency to clean up old unused event handlers. Plus, for example, this allows you to have the same button performing different actions in different circumstances - all you have to do is add or remove event handlers as appropriate.

    Second, you can also register multiple handlers for the same listener. The following two handlers wouldn't both be applied:

    MyElement.onclick = functionA; myElement.onclick = functionB;

    The second line overwrites the value of onclick set by the first line. This would work, however:

    MyElement.addEventListener("click", functionA); myElement.addEventListener("click", functionB);

    Both functions would now run when the element is clicked.

    In addition, there are a number of other powerful features and options available with this event mechanism. These are a little out of scope for this article, but if you want to read up on them, have a look at the addEventListener() and removeEventListener() reference pages.

    What mechanism should I use?

    Of the three mechanisms, you definitely shouldn't use the HTML event handler attributes - these are outdated, and bad practice, as mentioned above.

    The other two are relatively interchangeable, at least for simple uses:

    • Event handler properties have less power and options, but better cross-browser compatibility (being supported as far back as Internet Explorer 8). You should probably start with these as you are learning.
    • DOM Level 2 Events (addEventListener() , etc.) are more powerful, but can also become more complex and are less well supported (supported as far back as Internet Explorer 9). You should also experiment with these, and aim to use them where possible.

    The main advantages of the third mechanism are that you can remove event handler code if needed, using removeEventListener() , and you can add multiple listeners of the same type to elements if required. For example, you can call addEventListener("click", function() ( ... )) on an element multiple times, with different functions specified in the second argument. This is impossible with event handler properties because any subsequent attempts to set a property will overwrite earlier ones, e.g.:

    Element.onclick = function1; element.onclick = function2; etc.

    Note: If you are called upon to support browsers older than Internet Explorer 8 in your work, you may run into difficulties, as such ancient browsers use different event models from newer browsers. But never fear, most JavaScript libraries (for example jQuery) have built-in functions that abstract away cross-browser differences. Don't worry about this too much at this stage in your learning journey.

    Other event concepts

    In this section, we briefly cover some advanced concepts that are relevant to events. It is not important to understand these concepts fully at this point, but they might serve to explain some code patterns you"ll likely come across from time to time.

    Event objects

    Sometimes inside an event handler function, you might see a parameter specified with a name such as event , evt , or simply e . This is called the event object , and it is automatically passed to event handlers to provide extra features and information. For example, let's rewrite our random color example again slightly:

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

    Here you can see that we are including an event object, e , in the function, and in the function setting a background color style on e.target - which is the button itself. The target property of the event object is always a reference to the element that the event has just occurred upon. So in this example, we are setting a random background color on the button, not the page.

    Note : You can use any name you like for the event object - you just need to choose a name that you can then use to reference it inside the event handler function. e/evt/event are most commonly used by developers because they are short and easy to remember. It"s always good to be consistent - with yourself, and with others if possible.

    e.target is incredibly useful when you want to set the same event handler on multiple elements and do something to all of them when an event occurs on them. You might, for example, have a set of 16 tiles that disappear when they are clicked on. It is useful to always be able to just set the thing to disappear as e.target , rather than having to select it in some more difficult way. In the following example (see useful-eventtarget.html for the full source code; also see it running live here), we create 16 elements using JavaScript. We then select all of them using document.querySelectorAll() , then loop through each one, adding an onclick handler to each that makes it so that a random color is applied to each one when clicked:

    Const divs = document.querySelectorAll("div"); for (let i = 0; i< divs.length; i++) { divs[i].onclick = function(e) { e.target.style.backgroundColor = bgChange(); } }

    The output is as follows (try clicking around on it - have fun):

    Hidden example Useful event target example div ( height: 100px; width: 25%; float: left; ) for (let i = 1; i
  • Pencil
  • Pen
  • eraser

  • Now that we know that any click on the button will pop up through the ul.toolbar element, let's attach our event handler to it. Fortunately, we already have it:

    Var toolbar = document.querySelector(".toolbar"); toolbar.addEventListener("click", function(e) ( var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList. remove("active"); ));
    We now have much cleaner code, and we've even gotten rid of loops! Note however that we have replaced e.currentTarget with e.target . The reason lies in the fact that we process events at a different level.

    e.target is the actual target of the event, where it makes its way through the DOM, and where it will then bubble up from.
    e.currentTarget - the current element that handles the event. In our case, this is ul.toolbar.

    Improved popup events Currently we handle any click on each element that pops up via ul.toolbar , but our validation condition is too simple. What would happen if we had a more complex DOM that included icons and elements that were not designed to be clicked?

    • Pencil
    • Pen
    • eraser

    Oops! Now when we click on the li.separator or icon, we add the .active class to it. At the very least, this is not good. We need a way to filter events so that we react to the element we need.

    Let's create a small helper function for this:

    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)); ); );
    Our assistant does two things. First, it will iterate over each element and its parents and check if they satisfy the condition passed in the criteria parameter. If the element satisfies, the helper adds a field to the event object called delegateTarget, which stores the element that satisfies our conditions. And then calls the handler. Accordingly, if no element satisfies the condition, no handler will be called.

    We can use it like this:

    Var toolbar = document.querySelector(".toolbar"); var buttonsFilter = function(elem) ( return 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("click", delegate(buttonsFilter, buttonHandler));
    Just what the doctor ordered: one event handler attached to one element that does all the work. But it does it only for the elements we need. And it responds perfectly to adding and removing objects from the DOM.

    Summing up We briefly reviewed the basics of implementing delegation (handling pop-up) events on pure JavaScript. This is good because we don't need to generate and attach a bunch of handlers for each element.

    If I wanted to make a library out of this or use the code in development, I would add a couple of things:

    Helper function to check whether an object meets the criteria in a more unified and functional form. Like:

    Var criteria = ( isElement: function(e) ( return e instanceof HTMLElement; ), hasClass: function(cls) ( return function(e) ( return criteria.isElement(e) && e.classList.contains(cls); ) ) //More criteria);
    Partial use of the assistant would also be useful:

    Var partialDelgate = function(criteria) ( return function(handler) ( return delgate(criteria, handler); ) );
    Original article: Understanding Delegated JavaScript Events
    (From the translator: my first, judge strictly.)

    Happy coding!



    tell friends