最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Write a custom event dispatcher in JavaScript? - Stack Overflow

programmeradmin0浏览0评论

How can I add event listeners to and dispatch events from my objects of my own classes in JavaScript?

In ActionScript 3 I can simply inherit from Sprite/DisplayObject and use the methods available there. Like this:

// ActionScript-3:
// I can add event listeners to all kinds of events
var mySprite: Sprite = new MySprite();
mySprite.addEventListener("my_menu_item_click", onMenuItemClick);

// later I can dispatch an event from one of my objects
mySprite.dispatchEvent(new Event("my_menu_item_click", ...));

I would like to have the same in JavaScript. Until now I know about window.addEventListener(...) and document.addEventListener(...). I have my own Sprite class in JavaScript so far and I want to use it to dispatch my own events.

// JavaScipt:
function Sprite()
{
    this.x = 0;
    this.y = 0;
    // ...
}

Since both languages seem so "alike" with events I guess I need to inherit from some class? Or do I have to use some of the global variables like window and/or document?

I'm working with HTML5 here. I have only the 1 canvas and a bunch of sprites that are drawn to it and react to user input. I would like one sprite A to subscribe to events of another sprite B. But not to the third sprite C.

How can I add event listeners to and dispatch events from my objects of my own classes in JavaScript?

In ActionScript 3 I can simply inherit from Sprite/DisplayObject and use the methods available there. Like this:

// ActionScript-3:
// I can add event listeners to all kinds of events
var mySprite: Sprite = new MySprite();
mySprite.addEventListener("my_menu_item_click", onMenuItemClick);

// later I can dispatch an event from one of my objects
mySprite.dispatchEvent(new Event("my_menu_item_click", ...));

I would like to have the same in JavaScript. Until now I know about window.addEventListener(...) and document.addEventListener(...). I have my own Sprite class in JavaScript so far and I want to use it to dispatch my own events.

// JavaScipt:
function Sprite()
{
    this.x = 0;
    this.y = 0;
    // ...
}

Since both languages seem so "alike" with events I guess I need to inherit from some class? Or do I have to use some of the global variables like window and/or document?

I'm working with HTML5 here. I have only the 1 canvas and a bunch of sprites that are drawn to it and react to user input. I would like one sprite A to subscribe to events of another sprite B. But not to the third sprite C.

Share Improve this question edited Jan 3, 2021 at 13:14 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Jun 29, 2015 at 8:07 BitterblueBitterblue 14.1k18 gold badges92 silver badges130 bronze badges 1
  • Related: stackoverflow./questions/2059456/… – Silviu Burcea Commented Jun 29, 2015 at 8:11
Add a ment  | 

4 Answers 4

Reset to default 5

I tried to add the methods into my Sprite class and succeeded.
Of course not finished yet, but at least it works.
It's what I was looking for.

function Sprite()
{
    // ...
    this.eventListeners = new Array();

    this.addEventListener = function(type, eventHandler)
    {
        var listener = new Object();
        listener.type = type;
        listener.eventHandler = eventHandler;
        this.eventListeners.push(listener);
    }

    this.dispatchEvent = function(event)
    {
        for (var i = 0; i < this.eventListeners.length; i++)
            if (event.type == this.eventListeners[i].type)
                this.eventListeners[i].eventHandler(event);
    }
}

This is some kind of "functional" approach, I don't like using "new" and "this" in javascript. I like plain, peer or extended objects.

// Some helper functions, should be imported from a different file
const partial = fn => (...pargs) => (...args) => fn.apply(null, [...pargs, ...args]);
const partialRight = fn => (...pargs) => (...args) => fn.apply(null, [...args, ...pargs.reverse()]);

// Module starts here
const on = (listeners, type, listener, once) => {
  if (!listeners[type]) {
    listeners[type] = [];
  }
  if (listeners[type].indexOf(listener) < 0) {
    listener.once = once;
    listeners[type].push(listener);
  }
};

const once = partialRight(on)(true);

const off = (listeners, type, listener) => {
  const listenerType = listeners[type];
  if (listenerType && listenerType.length) {
    const index = listenerType.indexOf(listener);
    if (index !== -1) {
      listenerType.splice(index, 1);
    }
  }
  if ((listenerType && !listenerType.length) || !listener) {
    delete listeners[type];
  }
};

const emit = (listeners, type, ...data) => {
  if (listeners[type]) {
    listeners[type].forEach(listener => {
      listener.apply(null, data);
      if (listener.once) {
        off(listeners, type, listener);
      }
    });
  }
};

// you could use "export default () => {}" to make it an importable module
const eventEmitter = () => {
  const listeners = {};
  return {
    on: partial(on)(listeners),
    once: partial(once)(listeners),
    off: partial(off)(listeners),
    emit: partial(emit)(listeners),
  };
};

const myObj = Object.create(Object.assign({}, eventEmitter()));

myObj.on('hello', name => console.log(name));

setTimeout(() => {
  myObj.emit('hello', 'Csaba')
}, 2000)

You can do pretty much the same in JS with the Event and CustomEvent object :

var event = new Event('build');

// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);

// Dispatch the event.
elem.dispatchEvent(event);

Source : MDN (https://developer.mozilla/en-US/docs/Web/Guide/Events/Creating_and_triggering_events)

You can make your own, this one is generic and very simple. I actually use it myself :D

This is the functionality you'll get.

  • addEventListener()
  • removeEventlistener()
  • dispatchEvent() (Or what ever you wish to call it)

This is the function that will add the needed methods to your object and at the same time return a function that can dispatch events.

// Snippet  =========================================
function createEventDispatcher(o) {
  var L = o.__listeners = {};
  o.addEventListener = function(n, fn) { L[n] = L[n] || []; L[n].push(fn); };
  o.removeEventListener = function(n, fn) { var a = L[n]; for (var i = 0; i < a.length; i++) if (a[i] === fn) a.splice(i, 1);};
  return function() { var a = Array.prototype.slice.call(arguments); var l = L[a.shift()]; if (l)  for (var i = 0; i < l.length; i++) l[i].apply(l[i], a)};
}

:

// Simplified example of usage =========================================
function App(){
    // Add functionality
    var dispatchEvent = createEventDispatcher(this); // Call snippet

    // Use functionality
    var count = 0;
    setInterval(function(){ 
        dispatchEvent("something",{msg:"hello",count:count++});
    },100);
}();

// Somewhere outside your App    
    function onSomething(event){
        console.log(event.msg + "["+event.count+"]");
        if(event.count >= 10){ 
            // Remove eventlistener 
            myApp.removeEventListener("something",onSomething);
        } 
   }

    var myApp = new App();    
    myApp.addEventListener("something",onSomething);

// Easy

https://jsfiddle/squadjot/0n2nby7k/

If you want to remove all listeners from your object, you can simply do something like.

myApp.__listeners = {};
发布评论

评论列表(0)

  1. 暂无评论