5

I've created a Javascript pseudoclass and I want it to be able to dispatch custom events. Unfortunately, it seems like only DOM nodes are able to dispatch events. So, I'm forced to associate a DOM node with every instance of my pseudoclass, so that the DOM node can dispatch events on behalf of my class.

I'd like to be able to add event listeners directly to instances of my pseudoclass. Unfortunately, because the pseudoclass isn't able to dispatch it's own events, I must add the event listeners to the associated DOM nodes instead.

When I do this, my event handler functions receive a reference to the DOM node instead of the pseudoclass instance. I must then determine which pseudoclass instance the DOM node belongs to, before I can call it's methods.

I'm trying to maintain a clean separation between my Javascript and the DOM, wherever possible. Of course there will be times when it will make sense to associate pseudoclass instances with DOM elements, but I don't want this to be an absolute requirement.

My boilerplate code:

jQuery(document).ready(function($){

    var cat = new Animal($('#cat'), 'Larry', 'Meow');

    cat.domElement.on('spoken', function(event){
        var classInstance = $(this).data('instance');
        console.log(classInstance.firstName + ' has spoken');
    });

    cat.speak();

});

My pseudoclass

(function(){

    window.Animal = Animal;

    function Animal(domElement, firstName, sound) {
        // Save a reference to the DOM node
        this.domElement = domElement;

        // Give the DOM node a reference to this class instance
        this.domElement.data('instance', this);

        this.firstName = firstName;
        this.sound = sound;
    }

    Animal.prototype.speak = function() {
        console.log(this.sound);
        this.domElement.trigger('spoken');
    };

}(window));

Side note: I need to support legacy browsers such as IE8. I tried very hard to use the proper Javascript event model, instead of having to rely on jQuery. Unfortunately, I wasn't able to find adequate polyfills for dispatchEvent, addEventListener, removeEventListener, Event, CustomEvent, etc. jQuery provides an excellent abstraction layer with familiar syntax, and my team uses it regularly anyway. However, please let me know if there's a clean, vanilla Javascript solution!

MarkPlewis
  • 2,310
  • 1
  • 15
  • 13
  • 1
    will this help your understanding ? http://stackoverflow.com/questions/9671995/javascript-custom-event-listener – Mindeater May 15 '14 at 23:00
  • 1
    What an interesting question. It made me think of [**getters and setters**](http://ejohn.org/blog/javascript-getters-and-setters/), is this link of any use? – user115014 May 15 '14 at 23:07
  • possible duplicate: http://stackoverflow.com/questions/6635138/is-it-possible-to-dispatch-events-on-regular-objects-not-dom-ones?rq=1 and as said there, you can't do this using the native APIs and you simply will have to make an emulation, which somebody provided very nicely there – markasoftware May 15 '14 at 23:07
  • Oh yes, it was [publishers and subscribers](https://github.com/mroderick/PubSubJS) not getters and setters :) – user115014 May 15 '14 at 23:11
  • Thanks Mindeater. I suppose I could dispatch all of my events from the document object instead of lower-level DOM nodes. This would eliminate the need to associate DOM nodes with my pseudoclass instances. I'll still need to pass along a reference to the pseudoclass instance though, so that the event handler can determine which object triggered the event. – MarkPlewis May 15 '14 at 23:14

2 Answers2

2

You can emulate simple event handling relatively easy by yourself:

function Animal(domElement, firstName, sound) {
    this.$$handler = {};
}

Animal.prototype.on = function (eventType, callback) {
    if (!this.$$handler[eventType]) {
        this.$$handler[eventType] = [];
    }
    this.$$handler[eventType].push(callback);
};

Animal.prototype.trigger = function (eventType, args) {
    var i, max_i,
        handler = this.$$handler[eventType];

    if (!handler) {
        return;
    } 
    for (i = 0, max_i = handler.length; i < max_i; i++) {
        handler[i](args);
    }

};

Animal.prototype.speak = function() {
    this.trigger('spoken', {"eventargs": []});
};


//on the instance
cat.on('spoken', function(args){
    var classInstance = $(this).data('instance');
    console.log(classInstance.firstName + ' has spoken');
});
basilikum
  • 10,378
  • 5
  • 45
  • 58
  • The OP wants to get away from the DOM and would like a "clean, vanilla Javascript solution". – RobG May 15 '14 at 23:46
  • @RobG where exactly do I refer to the DOM? – basilikum May 16 '14 at 12:50
  • I presumed the OP was using `function Animal(domElement,...)` only to leverage the DOM event model, so there should be no *domElement* or `$(his)`. – RobG May 16 '14 at 22:56
1

You don't have to use the DOM as your event generating/handling mechanism. You can write your own, or use something like Backbone.Events which is designed to be mixed into any class.

user229044
  • 232,980
  • 40
  • 330
  • 338