How to use JavaScript EventTarget?

前端 未结 10 1767
一生所求
一生所求 2020-12-01 03:53

I would like to create a custom event emitter in my client-side programs. I am referencing this (sparse) documentation for EventTarget

My implementation atte

相关标签:
10条回答
  • 2020-12-01 04:24

    Bergi was right about the part, that EventTarget is just an interface and not a constructor.

    There are multiple objects in js that are valid event targets. As mentioned there: Element, document, and window are the most common event targets, but there are also others for example Websocket. Anyway, all of them are given.

    If you make a short test, you can notice few things:

    EventTarget.isPrototypeOf(WebSocket); // true
    
    var div = document.createElement("div");
    
    EventTarget.isPrototypeOf(div.constructor); // true
    
    typeof EventTarget // function
    
    EventTarget() // TypeError: Illegal constructor
    

    EventTarget is prototype of these constructors, which is something you can't set for any other constructor (and even if you could, it wouldnt probably work). Also it is a function, but not callable one.

    Now this is the time when you ask: So what is it EventTarget good for and how can I use it?

    We have 3 methods that each event emitter needs to implement and there was probably a need to bind these methods together, so we have an interface for them. Which means you can't use EventTarget for calling purposes, but some other native functions might. This is similar like creating elements, we have document.createElement factory method and we don't (can't) use new HTMLDivElement() to create a new element, but we can compare constructors of two elements.

    Conclusion

    If you want to create custom event emitter, you always have to create some dummy object or use some that already exists. From my point of view, it doesn't matter what object it will be.

    Some methods are not callable, but still can be compared as properties of objects. Therefore they are visible. EventTarget is one of them.

    0 讨论(0)
  • 2020-12-01 04:25

    There are 3 ways to achieve this depending on browser support.

    1) EventTarget is now constructable, so just extend it:

    class MyEventTarget extends EventTarget {
        constructor(){
            super()
        }
    }
    

    2) The DOM 'Node' interface implements EventTarget, so just implement that instead:

    function MyEventTarget(){
        var target = document.createTextNode(null);
        this.addEventListener = target.addEventListener.bind(target);
        this.removeEventListener = target.removeEventListener.bind(target);
        this.dispatchEvent = target.dispatchEvent.bind(target);
    }
    MyEventTarget.prototype = EventTarget.prototype;
    

    3) Roll your own (assuming no options arg) & dispatch async:

    function MyEventTarget(){
        this.__events = new Map();
    }
    MyEventTarget.prototype = {
        addEventListener(type, listener){
            var listeners = this.__events.get(type);
            if(!listeners){
                listeners = new Set();
                this.__events.set(type, listeners);
            }
            listeners.add(listener);
        },
    
        removeEventListener(type, listener){
            var listeners = this.__events.get(type);
            if(listeners){
                listeners.delete(listener);
                if(listeners.size === 0){
                    this.__events.delete(type);
                }
            }
        },
    
        dispatchEvent(event){
            var listeners = this.__events.get(event.type);
            if(listeners){
                for(let listener of listeners){
                    setTimeout(listener.call(null, event), 0);
                }
            }
        }
    }
    

    Replace Map()/Set() with {}/[] if required.

    All 3 of these options can be tested with:

    var target = new MyEventTarget();
    target.addEventListener('test', (e) => {console.log(e.detail);}, false);
    
    var event = new CustomEvent('test', {detail : 'My Test Event'});
    target.dispatchEvent(event);
    

    Any object that needs to implement your own 'EventTarget' interface can inherit it exactly as the native one does:

    function Person(name){
        MyEventTarget.call(this);
        this.__name = name;
    }
    Person.prototype = {
        __proto__ : MyEventTarget.prototype,
    
        get name(){ return this.__name;}
    }
    
    0 讨论(0)
  • 2020-12-01 04:25

    Here is how to do it using CustomEvent, cross-browser (fiddle):

    // listen to event
    window.addEventListener("say", function(e) { alert(e.detail.word); });
    
    // create and dispatch the event
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent('say', true, true, 
        { "word": "Hello!" });
    
    window.dispatchEvent(event);
    

    You'd need to use window or document or any other existing DOM element to register listeneres and dispatch the event. EventTarget is not a object, it's an interface. Try accessing EventTarget in JavaScript console and you'll see that.

    0 讨论(0)
  • 2020-12-01 04:26

    EventTarget is now specified as constructible in the DOM living standard. It is supported in Chrome 64 (already out) and Firefox 59 (coming March 13).

    0 讨论(0)
提交回复
热议问题