Using JavaScript Custom Events to Complete Data Communication between Components

We know that native DOM events are useful in JavaScript development (an important way to interact with users), but there are two major drawbacks to operating native DOM events: low performance, browser dependency (NodeJs, widgets, etc. are not available). At this point, we need to customize events to handle certain specific businesses.

The dispatchEvent Method for Understanding Event Objects and Elements

In JavaScript, the parent object of all events is Event object, that is to say, all event objects like click, touch, mouse and so on are inherited from Event. It is important to understand this. Let's start with a brief look at the scene of an event.

Scenario 1. There are two buttons a and b on the page. When the button b is clicked, the click event of button a is called. The simple layout code is as follows:

<button id="btn1">a</button>
<button id="btn2">b</button>

Programmer A takes these two buttons separately, then adds click event to button b and calls click method of button a. The code is as follows:

<button id="btn1" onclick="alert('I am a button named a')">a</button>
<button id="btn2">b</button>
<script>
    let btn1 = document.querySelector('#btn1');
    let btn2 = document.querySelector('#btn2');
    btn2.onclick = function(){
      btn1.onclick();
    }
</script>

Programmer B takes these two buttons separately, then adds click events to button b, and then adds click events to button a in the callback function. The code is as follows:

<button id="btn1">a</button>
<button id="btn2">b</button>
<script>
    let btn1 = document.querySelector('#btn1');
    let btn2 = document.querySelector('#btn2');
    btn2.onclick = function(){
      btn1.addEventListener('click',function(){
        alert('I am a button named a')
      },false)
    }
</script>

Looking at this, who do you think is doing the right thing? Apparently programmer A is right (as far as current requirements are concerned), but there is a drawback. If the event of button a is registered and monitored through the addEventListener method, it will not work. So what can we do better? This requires our dispatchEvent method for Event objects and elements. The improved code is as follows:

<button id="btn1">a</button>
<button id="btn2">b</button>
<script>
    let btn1 = document.querySelector('#btn1');
    let btn2 = document.querySelector('#btn2');
    btn1.addEventListener('click',function(){
        alert('I am a button named a')
      },false)
    btn2.onclick = function(){
      let ev = new Event('click');
      btn1.dispatchEvent(ev);
    }
</script>

Among them:

  • The constructor of an Event object requires a parameter, event type
  • The dispatchEvent method is to distribute an event to an element

Recognizing custom events

As mentioned earlier, in browser-side javascript, all events inherit from Event, so in order to implement a custom event, you also need to inherit from Event.

Or is it similar to the scenario mentioned above, scenario two: there are two buttons a and b, when the click event of the B button is called, how to trigger the custom event on the a button?

<button id="btn1">a</button>
<button id="btn2">b</button>
<script>
    let a = document.querySelector('#btn1');
    let b = document.querySelector('#btn2');
    a.addEventListener('myClick',function(){
        alert('I am a button named a')
    },false)
    class MyEvent extends Event{
        constructor(...args){
            super(...args);
            this.a = 12;
        }
    }
    b.onclick = function(){
        const ev = new MyEvent('myClick');
        a.dispatchEvent(ev);
    }
</script>

This is the Ideological Expression of custom events:

  • Registration of events according to event type
  • Distribution of corresponding events to the needy according to the type of event

It can be seen that it is necessary to manage events, such as EventBus in Java, $on, $emit in VueJs, etc. The event listener and distributor are abstracted into a separate module to manage events (add, remove, etc.) with decoupling.

Event queue completes communication between components

Event queues can be imagined as a pipeline, similar to the core idea of front-end gulp implementation (pipeline-based). When users need to use an event, they register an event in the pipeline, then distribute an event of this type from the pipeline through the event type, and remove the corresponding event without using it. The code is as follows:

class Pipe{
    constructor(){
        this.pipes = {};
    }
    /**
     * Registration event
     * @param {*} type 
     * @param {*} fn 
     */
    on(type,fn){
        this.pipes[type] = this.pipes[type] || [];
        if(this.pipes[type].findIndex(func => func==fn)==-1){
            this.pipes[type].push(fn);
        }
    }
    /**
     * Remove events
     * @param {*} type 
     * @param {*} fn 
     */
    off(type,fn){
        if(this.pipes[type]){
            this.pipes[type] = this.pipes[type].filter((func) => func!==fn);

            if(this.pipes[type].length===0){
                delete this.pipes[type];
            }
        }
    }
    /**
     * Distribution events
     * @param {*} type 
     * @param  {...any} args 
     */
    emit(type,...args){
        if(this.pipes[type]){
            this.pipes[type].forEach((fn) => {
                fn(...args);
            })
        }
    }
}

Scenario: Event queue is used to simulate the implementation of inter-component communication in vuejs. Component 1 is responsible for rendering data, and Component 2 clicks a button to change the value of Component 1 instance attribute a. The code is as follows:

<div id="box">
        {{a}}
</div>
<button id="btn1">++</button>
<script>
    const $ = document.querySelectorAll.bind(document);
    let pipe = new Pipe();
    class Component1{
        constructor(){
            this.a = 12;
            this.el = $("#box")[0];
            this.render();
            pipe.on('add',(arg) => {
                this.a+=arg;
                this.render();
            })
        }

        render(){
            this.el.innerHTML = this.a;
        }
    }

    class Component2{
        constructor(){
            this.el = $("#btn1")[0];
            this.el.onclick = function(){
                pipe.emit('add',12);
            }
        }
    }

    new Component1();
    new Component2();
</script>







Keywords: Javascript Java Attribute

Added by ratass2002 on Sun, 06 Oct 2019 21:11:13 +0300