DOM event system related basic skills, you need to master from the following aspects

  • The DOM event flow
  • The event object
  • The event agent
  • Custom events

The DOM event flow

Front knowledge

Before you can understand the flow of events, you need to understand the following three terms

  1. Event flow: This describes the order in which events propagate across the page.
  2. Events: This describes actions that take place in the browser. This action can be triggered by the user or the browser. Things like click, mouseover, and mousemove are all events.
  3. Event listener: How the browser responds to an event – the function used to respond to the event is called event listener, also known as event handler.

The evolution of the event flow

The current JS event flow specification, widely accepted by everyone, is not achieved overnight. In the early years, Internet Explorer and NetScape fought tooth and claw over the design of the event mechanism, with neither agreeing with the other. IE proposed bubbling streams, while NetScape only recognized captured streams. The two companies did their own jobs, so that front-end programmers had a very difficult time, every time to do web compatibility adaptation is a snot a tears. Fortunately, the righteous W3C stepped in, and under the unified organization of the W3C, JS supports both bubbling and capturing streams as the exact event flow standard. This standard is also called “DOM2 Event Flow”. We don’t talk about the non-standard, all of our discussion below, around this solid “DOM2 event flow” launched.

An event trip

The W3C standard dictates that the propagation of an event goes through three phases

  1. Event capture phase
  2. The target stage
  3. Event bubbling phase

The best way to understand this process is to read the graph, where the arrows represent the “travel” of time

It’s a lot like when you were a kid on a trampoline: you fall, you hit the trampoline, you bounce back up, and you go in a symmetrical “V” shape

When an event is triggered, it first goes through a capture process: the event “shuttles” from the outermost element to the innermost element. This shuttling process continues until the event reaches the element it is targeting (that is, the element that actually triggers the event). At this point, the event flow switches to the “target phase” where the event is received by the target element. The event then “bounces back” into the bubbling phase — it “goes upstream” the way it came, layer by layer, back again.

Event Object Basics

As an event travels through the layers of DOM elements, it doesn’t sit idle — wherever it goes, it triggers event handlers installed on the current element. For example, if you click on the button node shown above, the div node also has a click handler installed. So when the click event triggered by your button node passes through the div node, the handler on the div node will still fire.

When the DOM accepts an event and the corresponding event handler is fired, an event object is generated as an input parameter to the handler. This object contains information about the event, such as which element the event is triggered by, the type of event, and so on.

Now let’s write a simple HTML

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div class="outer">
    <button id="button">Click on the I</button>
  </div>
</body>
</html>
Copy the code

Let’s see what the button click event object looks like in this demo. We can write the corresponding handler like this

function clickProcessor(event) {
  console.log(event)
}
Copy the code

Now when we trigger a mouse click, the corresponding event object will look like this

We can see that in addition to the general basic information of an event, it also includes some supplementary information specific to a certain type of event (for example, for the click event, the information related to mouse position is recorded here). This event object is interesting, and we’ll focus on it.

Event object test point comb

In event objects, there are some properties and methods that we use in particular. This part of the thing is more broken, but more frequent. Interviewers will sometimes ask you individually, but more often they will look directly at coding questions to see if you can use them to write code. Let’s summarize this part as follows

  1. currentTarget

It records which element the event is currently being received by, i.e., which element it is passing through. This element is always changing because the propagation of events is, after all, a layered process.

If the event handler binds to the same element as the trigger element, the values of this, event.currenttarget, and event.target are the same. We can use this to determine whether the current element is the target element.

  1. target

The specific target that triggers the event, the most specific element, is the actual source of the event.

Even if the event handler is not bound to the target element but to the parent of the target element, as long as it is triggered by an internal target element bubbling up to the parent container, we can still sense through target that the target element is the actual source of the event.

  1. PreventDefault prevents the default behavior

This method is used to prevent the default behavior of certain events, such as the jump to the A tag. e.preventDefault();

  1. StopPropagation no longer distributes events

This method is used to terminate further propagation of events during the capture, target processing, or bubbling phases of propagation. When this method is called, the handler that handles the event on that node is called, and the event is no longer dispatched to another node. e.stopPropagation();

Sometimes we don’t want an event to trigger a “big bang” effect, we want to keep it within the target element. In this case, don’t forget stopPropagation.

  1. Event objects, which can be created manually

An Event object does not need to trigger a specific Event for it to “happen”; it can also be created manually: we can use the Event() constructor to create a new Event object, Event. event = new Event(typeArg, eventInit);

This property of event objects is the basis for creating custom events — custom events may be unfamiliar to some of you, but they’re really important. Four or five years ago, custom events were an important measure of how advanced a front end was. With the development of front-end technology, the demand for advanced front-end is increasing, and the ability to customize events becomes a basic level capability, but its indispensability remains unchangeable.

Custom events

Most of the events you’ve seen so far are related to browser behavior. Clicking a mouse, pressing a keyboard, etc., are sensed by the browser and help us translate into a “signal” that triggers the corresponding handler. But there are other behaviors that the browser doesn’t know about. Let’s say you look at some HTML

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    #divA.#divB.#divC {
      width: 100px;
      height:100px;
    }
    
    #divA {
      background-color: # 333;   
    }
    
    #divB {
      background-color: #dd5990; 
    }
    
    #divC {
      background-color: #ccc990; 
    }
  </style>
</head>
<body>
  <div id="divA">I am A</div>
  <div id="divB">I'm a B</div>
  <div id="divC">I am a C</div>
</body>
</html>
Copy the code

If we only want to listen for clicks on the divA element, we can use addEventListener to install the listener function

var divA = document.getElementById('divA')
document.addEventListener('click'.function(){
    console.log('I'm little A')})Copy the code

This is a very familiar operation. However, if I want to achieve such an effect now

After clicking on A, both B and C can sense that A has been clicked and act accordingly — just as the click event was on B and C.

Is that interesting? We know that by virtue of time capture and bubbling, we can achieve behavioral linkage between parent and child elements. But here, A, B, and C are on the same level. How can they sense what is happening to each other?

In fact, there are a lot of solutions to this problem, especially in the popular frame frequency today, I believe that we can associate with the way more. But in the early days, before engineers had so much to do with it, the classic solution to this problem was what we wrote in the title — custom events.

The event “A was clicked” can be dispatched as an event, and B and C listen for this event and execute the corresponding handlers installed on them. In this way, the action “A was clicked” is special, especially because the browser doesn’t recognize it. Because the browser doesn’t know it, it won’t “help” us by sensing and distributing the action. But that’s okay. Perception and distribution, we can do it ourselves

The first thing you need to know is the creation of custom events. Let’s say we want to create A non-existent “clickA” event to indicate that A was clicked

var clickAEvent = new Event('clickA');
Copy the code

OK, now that we have the event, let’s finish the listening and dispatching of the event

// Get the divB element
var divB = document.getElementById('divB')
// divB listens for clickA events
divB.addEventListener('clickA'.function(e){
  console.log('I'm little B, I feel little A')
  console.log(e.target)
}) 

// Get the divC element
var divC = document.getElementById('divC')
// divC listens for clickA events
divC.addEventListener('clickA'.function(e){
  console.log('I'm little C, I feel little A')
  console.log(e.target)
}) 

// The listener function for the A element also needs to be modified
divA.addEventListener('click'.function(){
  console.log('I'm little A')
  // Note that we dispatch events ourselves
  divB.dispatchEvent(clickAEvent)
  divC.dispatchEvent(clickAEvent)
})  
Copy the code

We can see the following output

That means our custom event is in effect!

As you may have noticed, we must get the exact elements divC and divA to install and distribute events. Sometimes, to further decouple, we might also consider stuffing all listeners into the document element, which can take different actions based on different types of custom events. This idea is very similar to event broker.

The event agent

Let’s talk about custom events in front, mainly for fear of falling into the pit. What does that mean? Personally, I rarely examine candidates’ custom events. In my opinion, I accept that as long as your JS foundation is good enough, this question must be ok (js foundation can be examined in other forms). However, for some interviewers who have strong native JS feelings, especially those who have written from prehistoric times to become a Team Leader and are not able to keep up with the trend, custom events can say a lot (after all, he wrote a lot in his early years). Let’s nip it in the bud and learn whether he wants to take the exam or not.

The frequency of custom events will vary from interviewer to interviewer. The event broker, on the other hand, is a good high-frequency test point. You are in my hands, I must ask you; If you fall into the hands of my colleagues, my colleagues will have to ask you. Many students think it is too basic, think it is to ask the question of fresh graduates. That’s half right — basic, yes, but basic doesn’t discriminate. So, event agents have to buck up and learn! (Knocks on the blackboard)

Event broker, also known as event delegate. If you didn’t know what it was before, don’t look up the concept now. Let’s just go ahead and do it

In the following HTML, I want to click on each li element to output its internal text. What would you do?

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <ul id="poem">
    <li>Goose goose goose</li>
    <li>Song to the sky</li>
    <li>White hair floating green water</li>
    <li>Anthurium flusher the waves</li>
    <li>Hoe Hoe Day</li>
    <li>Sweat dripped the soil</li>
    <li>Who knows what's on the plate</li>
    <li>Each is hard</li>
    <li>back</li>
    <li>I can't carry it</li>
  </ul>
</body>
</html>
Copy the code

An intuitive idea is to have each Li element listen for a click

<script>
  // Get the li list
  var liList = document.getElementsByTagName('li')
  // Install the listener functions one by one
  for (var i = 0; i < liList.length; i++) {
    liList[i].addEventListener('click'.function (e) {
      console.log(e.target.innerHTML)
    })
  }
</script>
Copy the code

If you do that, you won’t get a point. Keep in mind that the DOM interview questions fit the following three characteristics

  1. Want you to install a listener function that listens for an event (same event)
  2. Listeners are installed on a series of elements that have the same characteristics (elements have the same characteristics, generally the same parent elements)
  3. This set of listeners on the same characteristic elements all do the same thing.

At this time your mind must appear these four big words – event agent!

A: what happened? We have installed 10 listening functions for 10 Li. Are you tired? Is it expensive? If you think about it, all 10 listeners do the same thing. Can we converge this logic to a single element? The answer is yes, and why? Because something bubbled up! Do you think that clicking on any li will result in the click event being tagged with their common parent, ul? Can I get UL to do this? The answer is yes, because UL not only senses the event coming up, it also gets the element that actually triggered the event via E.target, making it a seamless proxy

var ul = document.getElementById('poem')
ul.addEventListener('click'.function(e){
  console.log(e.target.innerHTML)
}) 
Copy the code

Remember, e.target is the specific target that triggered the event. It records the source of the event. So, no matter what layer our listener is on, if I get this e.target, I get the element that actually triggered the event. Once we have this element, we can completely simulate its behavior and achieve an undifferentiated listening effect.

The action of taking advantage of the bubbling nature of events like this, combining the same type of listening logic of multiple child elements onto the parent element managed by a listener function, is event broker. By using the event broker, we can reduce memory overhead, simplify registration steps, and greatly improve development efficiency.