background

JavaScript is a language that uses a lot of event mechanisms, especially when interacting with the DOM. So it becomes essential to understand and understand the mechanics of events.

The way events are bound

<div onclick="fun();"> </div> //"xxx").onclick = function() {}; Element. addEventListener(<event-name>, <callback>, <use-capture>);Copy the code

When you use three at the same time, the second one overwrites the first one, which means that the first one and the second one belong to the same way, but are written differently. The reason is that there can only be one attribute, and repeated assignments overwrite the previous one. But addEventListener does not. This method binds to multiple event routines and executes them one by one.

The event triggers three phases

JavaScript event firing has three phases.

  • CAPTURING_PHASE: capture phase
  • AT_TARGET, the target phase
  • BUBBLING_PHASE

The event object has a parameter called eventPhase, which is a number indicating at which stage the event is triggered.

The definition of eventPhase can be found in the DOM Specification:

// PhaseType
const unsigned short      CAPTURING_PHASE                = 1;
const unsigned short      AT_TARGET                      = 2;
const unsigned short      BUBBLING_PHASE                 = 3;
Copy the code

Event passing diagram

Capture and bubble

Next, try adding events to each phase of each element and see if the result is as expected:

<! DOCTYPE html> <html> <body> <ul id="list">
    <li id="list_item">
      <span id="list_item_link" target="_blank" href="http://google.com">
        click me
      </span>
    </li>
  </ul>
</body>
<script>
    const get = (id) => document.getElementById(id);
    const $list = get('list');
    const $list_item = get('list_item');
    const $list_item_link = get('list_item_link'); // List capture$list.addEventListener('click', (e) => {
      console.log('list capturing', e.eventPhase);
    }, true// List bubbles$list.addEventListener('click', (e) => {
      console.log('list bubbling', e.eventPhase);
    }, false// list_item capture$list_item.addEventListener('click', (e) => {
      console.log('list_item capturing', e.eventPhase);
    }, true// list_item bubbles$list_item.addEventListener('click', (e) => {
      console.log('list_item bubbling', e.eventPhase);
    }, false// list_item_link$list_item_link.addEventListener('click', (e) => {
      console.log('list_item_link capturing', e.eventPhase);
    }, true// list_item_link bubbles$list_item_link.addEventListener('click', (e) => {
      console.log('list_item_link bubbling', e.eventPhase);
    }, false)
</script>
</html>
Copy the code

Output result:

list capturing 1
list_item capturing 1
list_item_link capturing 2
list_item_link bubbling 2
list_item bubbling 3
list bubbling 3
Copy the code

1 is CAPTURING_PHASE, 2 is AT_TARGET, and 3 is BUBBLING_PHASE.

Listeners registered at target do not capture or bubble

When the event is passed to the actual click object, e.target, the e.ventPhase here becomes AT_TARGET regardless of whether you use the third addEventListener parameter, true or false.

const get = (id) => document.getElementById(id);
const $list = get('list');
const $list_item = get('list_item');
const $list_item_link = get('list_item_link'); // List bubbles$list.addEventListener('click', (e) => {
  console.log('list bubbling', e.eventPhase);
}, false) // List capture$list.addEventListener('click', (e) => {
  console.log('list capturing', e.eventPhase);
}, true// list_item bubbles$list_item.addEventListener('click', (e) => {
  console.log('list_item bubbling', e.eventPhase);
}, false// list_item capture$list_item.addEventListener('click', (e) => {
  console.log('list_item capturing', e.eventPhase);
}, true// list_item_link bubbles$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link bubbling', e.eventPhase);
}, false// list_item_link$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link capturing', e.eventPhase);
}, true)
Copy the code
list capturing 1
list_item capturing 1
list_item_link bubbling 2
list_item_link capturing 2
list_item bubbling 3
list bubbling 3
Copy the code

Just keep two principles in mind about the order in which these events are delivered:

  • Capture first, then bubble
  • When the event is passed to the target itself, there is no capture or bubbling. What is added first is executed first, and what is added later is executed later

Target and currentTarget

With the three phases of event passing described above, let’s tease out two confusing attributes in the event object: Target and currentTarget.

  • A target is a specific object that triggers an event and only appears in the target phase of the event mechanism, i.e., “Whoever triggered the event is target.”
  • CurrentTarget is an object to which events are bound.

Cancel event passing

We can pass interrupt events down or up through E. topPropagation.

// List capture$list.addEventListener('click', (e) => {
  console.log('list capturing');
  e.stopPropagation();
}, true);
Copy the code
list capturing
Copy the code

As you can see, event propagation is interrupted and the remaining listener cannot receive the event. However, note that stopPropagation does not prevent the execution of other listeners on the same node. Such as:

// List capture$list.addEventListener('click', (e) => {
  console.log('list capturing');
  e.stopPropagation();
}, true); // List of capture 2$list.addEventListener('click', (e) => {
  console.log('list capturing 2');
}, true);
Copy the code

Then the output result is:

list capturing
list capturing 2
Copy the code

Copy the code for the same node other listener is not implemented, we can use e.s topImmediatePropagation method.

Unpreset behavior

We can use e.preventDefault to cancel the default behavior.

<a id="list_item_link" href="#">click me</a> // list_item_link bubble$list_item_link.addEventListener('click', (e) => {
  e.preventDefault();
}, false);
Copy the code

This way, when we click on a hyperlink, we don’t perform the original default behavior (open a new page or jump). Many people will confuse E. Strand Propagation with E. preventdefault. In fact, E. preventDefault has nothing to do with event propagation and does not affect the propagation of events down or up.

Here’s a particularly noteworthy one from the W3C.

Once preventDefault has been called it will remain inEffect throughout the remainder of the event's propagation.Copy the code

The preventDefault method will have the effect if it is passed on to new events as long as it is called.

// List bubbles$list.addEventListener('click', (e) => {
  console.log('list bubbling', e.eventPhase);
  e.preventDefault();
}, true);
Copy the code

The result is that the default behavior of the hyperlink is not being implemented. Note that the preventDefault method should be used to cancel the default behavior, whether in capture or bubbling.

The event agent

When we want to add 1000 Li to ul nodes, if we add eventListener to each li, we create 1000 functions. However, through the event propagation mechanism, we can register eventListener with UL. There are bright spots:

  • Save memory
  • There is no need to unregister events for child nodes

The order in which events are executed

  • The code in the href of the A tag is always executed last, lowest priority.
  • Both onclick and addEventListener are executed in the order of the binding, that is, the first binding is executed.
  • If the onClick event is bound repeatedly, it is in the order in which it was last bound.
  • If onclick is used directly in the DOM and is not overridden, the onclick binding precedes the addEventListener.
  • If multiple addEventListener events are bound, stopPropagation() in any one event; Prevents events from bubbling, but does not prevent subsequent events from executing.

The event list

You can use MDN or enter the following information in the browser:

for (i in window) {
  if( /^on/.test(i)) { console.log(i); }}Copy the code

Check it out and you’ll find more events on offer than you thought!

Common event
  • Load: Triggered when the resource is loaded. This resource can be an image, CSS file, JS file, video, document, window, and so on.
  • Raised when the DOMContentLoaded DOM has been built, which is what jQuery’s Ready method wraps.
  • Beforeunload: When a viewer enters something in an input field on a page, unsaved or accidentally closed the page may result in lost input information. We can start listening for this event when the viewer closes the page without saving it. When attempting to close the page, a pop-up window will block the action and the page will be closed only after clicking OK.
  • Resize: This event is triggered when the node size changes. Usually used on Windows to listen for changes in the browser window.
  • Error: When a resource fails to load or is successfully loaded but only part of it cannot be used, an error event is raised. We can listen for this event to prompt a friendly error or do other processing. For example, JS resources fail to be loaded. The image resource fails to be loaded. A message is displayed under the image indicating that the image fails to be loaded. The event does not bubble. Just because the child node failed to load does not mean that the parent node failed to load, so your handler must bind exactly to the target node.

Explain an event object

Partial fields of a MouseEvent object:

  • “ScreenX”, “screenY”, “clientX”, “clientY”, set the coordinate position of the mouse event
  • “CtrlKey”, Boolean optional, default false, indicates whether CTRL key is pressed at the same time.
  • “ShiftKey”, Boolean Optional, default to false, indicating whether shift key is also pressed.
  • “AltKey”, Boolean optional, default to false, indicating whether Alt key is also pressed.
  • “MetaKey”, Boolean optional. Default is false, indicating whether meta key is pressed at the same time.

– MouseEvent. Prototype has no stopPropagation or preventDefault methods.

Actually, topPropagation and preventDefault are the Event methods

Using MouseEvent.__proto__ you can see that MouseEvent is derived from UIEvent. You can see the same thing. UIEvent is derived from Event. Some specific events are derived from mouseEvents: wheelEvents and DragEvents

Event handling in Vue

Listen for an event

Vue can listen for DOM events with v-ON directives and run some JavaScript code when triggered.

<div id="example-1">
  <button v-on:click="doSomething">do something</button>
</div>
Copy the code
Event modifier

Calling event.preventDefault() or event.stopPropagation() in the event handler is a very common requirement. While this could easily be done in a method, it’s better if the method has pure data logic instead of dealing with DOM event details.

To solve this problem, vue.js provides event modifiers for V-Ons. As mentioned earlier, modifiers are represented by an instruction suffix beginning with a dot.

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<! <a V-on :click.stop="doThis"></a> <! <form V-on :submit. Prevent ="onSubmit"></form> <! <a V-on :click.stop.prevent="doThat"></a> <! -- Use event capture mode when adding event listeners --> <! <div V-on :click.capture= <div V-on :click.capture= <div v-on:click.capture="doThis">... </div> <! Trigger handler only if event.target is the current element itself --> <! <div v-on:click.self= <div v-on:click.self="doThat">... </div>Copy the code

When using modifiers, order is important; The corresponding code is generated in the same order. Therefore, using V-on :click.prevent. Self blocks all clicks, whereas V-on :click.self. Prevent only blocks clicks on the element itself.

extension

AddEventListener and removeEventListener

target.addEventListener(type, listener[, useCapture]);

  • True – Event handles are executed during the capture phase
  • False – the default. The event handle executes during the bubbling phase

target.removeEventListener(event, function[, useCapture])

  • The function must be. Specifies the function to remove.

The Vue$on$off

  • vm.$emit(eventName,callback)$on(eventName,callback)Generally used in combination. useOn listens for the event and invokes the callback function. These two event methods can be combined with the props property to implement the parent component bidirectional parameter transfer.

  • Vm.$once(eventName,callback) listens for a custom event, but fires only once, and removes the listener after the first time.

  • $off([eventName,callback]) is used to remove the custom event listener. If no arguments are provided, all event listeners are removed; If only events are provided, remove all listeners for that event; If both an event and a callback are provided, only the listener for that callback is removed.

  • Vue.js source code – event mechanism

The resources

  • DOM’s event-passing mechanism: capture and bubbling
  • Event
  • w3c Events Doc