The event

Generally speaking, an event is an action performed by the user or the browser itself. It is a moment of interaction between a document or browser, such as a click button. Click is the name of the event. The interaction between JS and HTML is achieved through events.

Each event has an event listener (sometimes called an event listener), which is a block of code that runs when the event is triggered. Strictly speaking, event listeners listen for events to occur, and event handlers react to events.

The event model

Propagation after the occurrence of an event, propagation between child elements and parent elements will be divided into three stages.

There are three stages of DOM event propagation:

  • Capture phase
  • The target stage
  • Bubbling phase

Event capture and event bubbling

  • Event capture: Look for listener functions from the outside in.
  • Event bubbling: Find listener functions from inside out.

Example:

<div class="grandfather">
  <div class="father">
    <div class="son"></div>
    hi
  </div>
</div>
// Add event listener fnYe/fnBa/fnEr to each div
Copy the code

Problem a:

  • Click on the text, calculate not click son?
  • Click on the text, does that count as clicking dad?
  • Click text, does it count as click grandpa?

Answer: Both

Q2: What is the order in which listener functions are called?

Event capture: fnYe > fnBa > fnEr, that is, call from outside to inside

Event bubble: fnEr > fnBa > fnYe, that is, call from inside out

The W3C published the standard in 2002 as the DOM Level 2 Events Specification

Specifies that the browser supports both call sequences

First according to grandpa -> dad -> son order to see if there is a function listening, first event capture

Then in the order of son -> father -> grandfather to see if there is a function listening, and then the event bubbling

As shown in the figure:

How do YOU specify walk capture or bubble?

baba.attachEvent('onclick',fn)/ / the bubbling
baba.addEventListener('click',fn)/ / capture
baba.addEventListener('click',fn,bool)/ / the w3c
Copy the code
  • If bool is not passed or falsy
  • Let the FN bubble away when the browser finds it in the bubble phasebabafnListen to the function, which calls fn and provides event information.
  • If bool is true
  • Let fn go capture, that is, when the browser finds it in the capture phasebabafnThe listener function will call fn and provide event information.

Code examples:

HTML:

<div class="level1 x">
  <div class="level2 x">
    <div class="level3 x">
      <div class="level4 x">
        <div class="level5 x">
          <div class="level6 x">
            <div class="level7 x">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
Copy the code

CSS:

* {
  box-sizing: border-box;
}
div[class^=level] {
  border: 1px solid;
  border-radius: 50%;
  display: inline-flex;
}
.level1 {
  padding: 10px;
  background: purple;
}
.level2 {
  padding: 10px;
  background: blue;
}
.level3 {
  padding: 10px;
  background: cyan;
}
.level4 {
  padding: 10px;
  background: green;
}
.level5 {
  padding: 10px;
  background: yellow;
}
.level6 {
  padding: 10px;
  background: orange;
}
.level7 {
  width: 50px;
  height: 50px;
  border: 1px solid;
  background: red;
  border-radius: 50%;
}
.x{
  background: transparent;
}
Copy the code

JS:

const level1 = document.querySelector('.level1')
const level2 = document.querySelector('.level2')
const level3 = document.querySelector('.level3')
const level4 = document.querySelector('.level4')
const level5 = document.querySelector('.level5')
const level6 = document.querySelector('.level6')
const level7 = document.querySelector('.level7')
let n = 1
 const fm = (e) = >{
  const t = e.currentTarget
  setTimeout(() = >{  
    t.classList.remove('x')
  },n*1000)
  n+=1
}
 const fa = (e) = >{
   const t =e.currentTarget
   setTimeout(() = >{
     t.classList.add('x')
   },n*1000)
   n+=1
 }
level1.addEventListener('click',fm,true) / / true capture
level1.addEventListener('click',fa) // The default is false
// Capture then bubble
level2.addEventListener('click',fm,true)
level2.addEventListener('click',fa)
level3.addEventListener('click',fm,true)
level3.addEventListener('click',fa)
level4.addEventListener('click',fm,true)
level4.addEventListener('click',fa)
level5.addEventListener('click',fm,true)
level5.addEventListener('click',fa)
level6.addEventListener('click',fm,true)
level6.addEventListener('click',fa)
level7.addEventListener('click',fm,true)
level7.addEventListener('click',fa)
Copy the code

A special case

// Only one div is listened on.
div.addEventListenter('click',f1) / / the bubbling
div.addEventListenter('click',f2,true) / / capture
Copy the code

f1Do it first orf2What about executing first?

First capture in bubbling, f2 first execution?

Correct answer: F1 is executed first.

When there is no father-son relationship, whoever listens first executes first.

Differences between Target and currentTarget

  • e.targetElements of user action
  • e.currentTargetThe element that the programmer listens for

Example:

<div>
  <span>The text</span>
</div>
Copy the code
  • e.targetisspan, the element for user action.
  • e.currentTargetisdiv, the element that the programmer listens for.

Cancel the bubbling

E.toppropagation () interrupts bubbling and the browser will no longer go up

Generally used to encapsulate some independent components

Note: capture cannot be undone but bubbling can (some events cannot be undone either, such as the Scroll bar)

Event delegation

Benefits of event delegation:

  1. Provincial storage (number of provincial monitoring)
  2. You can listen for dynamic elements

Example 1:

// There are 100 buttons<div id="div1">
  <span>span1</span>
   <button>click 1</button>
   <button>click 2</button>
   <button>click 3</button>
   <button>click 4</button>
   <button>click 5</button>.<button>click 100</button>
</div>
Copy the code

What if I have 100 buttons? You can’t create 100 listeners.

Delegate listening to div

We just need to listen for the ancestor of the 100 buttons and wait for the bubble to determine if E.target is one of the 100 buttons to save memory.

div1.addEventListener('click'.(e) = >{
  const t = e.target
  if(t.tagName.toLowerCase()==='button') {console.log('Button is clicked')}})Copy the code

Example 2:

How do we listen when button appears after a 1 second delay?

// After a delay of 1 second, create button
setTimeout(() = >{
  const button = document.createElement('button')
  button.textContent='click'
  div1.appendChild(button)
},1000)

div1.addEventListener('click'.(e) = >{
  const t = e.target
  if(t.tagName.toLowerCase()==='button') {console.log('Button clicked')}})Copy the code

Listen for ancestors, wait until you click to see if it’s listening to the element, listen for dynamic elements.

Encapsulating event delegate

setTimeout(() = >{
  const button = document.createElement('button')
  button.textContent='click'
  div1.appendChild(button)
},1000)

on('click'.'#div1'.'button'.() = >{  //'#div' is a selector, not an element
  console.log('Button clicked')})// Declare the on function
function on(eventType,element,selector,fn){
// Determine if element is an element
  if(! (elementinstanceof Element)){
       // If not, find the specified element and assign it to element
       element = document.querySelector(element)
     }
  element.addEventListener(eventType,(e) = >{
  const t= e.target
  if(t.matches(selector)){ 
    fn(e)
   }
})
}
Copy the code