Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing events in custom object

What I want to have is a custom object which provides some events. For example:

var CustomObjectTextChangedEventName = 'textChanged';
var CustomObject = function () {
    var _this = this;
    var _text = "";

    _this.OnTextChanged = document.createEvent("Event");
    _this.OnTextChanged.initEvent(CustomObjectTextChangedEventName, true, false);

    _this.ChangeText = function (newText) {
        _text = newText;
        fireTextChanged();
    };

    function fireTextChanged() {
        _this.dispatchEvent(_this.OnTextChanged);
    }
}

The code to use the event would look like:

myCustomObject = new CustomObject();
myCustomObject.addEventListener(CustomObjectTextChangedEventName, handleTextChanged, false);

As you can see... the default way of using events in JS, but I can not make it work...

Currently my problem is that my object does not implement addEventListener and dispatchEvent, but this functions are normally implemented from "element"...

Can I make them available somehow or do I have to implement them for my own? How I have to implement them?

Do I have to implement my own event handling? (having a internal list of handlers, an "add"- and "remove"-handler function, and fire each handler when I want to fire the event)?

like image 361
Dominik Kirschenhofer Avatar asked Jun 11 '12 10:06

Dominik Kirschenhofer


2 Answers

The addEventListener function is a method of Element class. One way is to make CustomObject inherit from Element like this:

CustomObject.prototype = Element.prototype;

The problem is that Element class may have different implementations among different browsers. So for example firing events may not be easy (see this post).

So I advice doing this by yourself. It is not difficult, try something like this:

var CustomObject = function () {
    var _this = this;
    _this.events = {};

    _this.addEventListener = function(name, handler) {
        if (_this.events.hasOwnProperty(name))
            _this.events[name].push(handler);
        else
            _this.events[name] = [handler];
    };

    _this.removeEventListener = function(name, handler) {
        /* This is a bit tricky, because how would you identify functions?
           This simple solution should work if you pass THE SAME handler. */
        if (!_this.events.hasOwnProperty(name))
            return;

        var index = _this.events[name].indexOf(handler);
        if (index != -1)
            _this.events[name].splice(index, 1);
    };

    _this.fireEvent = function(name, args) {
        if (!_this.events.hasOwnProperty(name))
            return;

        if (!args || !args.length)
            args = [];

        var evs = _this.events[name], l = evs.length;
        for (var i = 0; i < l; i++) {
            evs[i].apply(null, args);
        }
    };
}

Now using it is as simple as:

var co = new CustomObject();
co.addEventListener('textChange', function(name) {
    console.log(name); 
});
co.fireEvent('textChange', ['test']);

This is a basic solution. You may want to alter it, but I think you should grasp the idea.

like image 73
freakish Avatar answered Oct 08 '22 10:10

freakish


I'm not sure on all 100% but next is a result of my old research within this problem:

  • You cannot make available somehow this.
  • You can simply implement your own logic. For this you can use code that exist in MDN element.removeEventListener article with little changes. Below it copy\past of code from MDN link:

// code source: MDN: https://developer.mozilla.org/en/DOM/element.removeEventListener
// without changes
if (!Element.prototype.addEventListener) {  
  var oListeners = {};  
  function runListeners(oEvent) {  
    if (!oEvent) { oEvent = window.event; }  
    for (var iLstId = 0, iElId = 0, oEvtListeners = oListeners[oEvent.type]; iElId < oEvtListeners.aEls.length; iElId++) {  
      if (oEvtListeners.aEls[iElId] === this) {  
        for (iLstId; iLstId < oEvtListeners.aEvts[iElId].length; iLstId++) { oEvtListeners.aEvts[iElId][iLstId].call(this, oEvent); }  
        break;  
      }  
    }  
  }  
  Element.prototype.addEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {  
    if (oListeners.hasOwnProperty(sEventType)) {  
      var oEvtListeners = oListeners[sEventType];  
      for (var nElIdx = -1, iElId = 0; iElId < oEvtListeners.aEls.length; iElId++) {  
        if (oEvtListeners.aEls[iElId] === this) { nElIdx = iElId; break; }  
      }  
      if (nElIdx === -1) {  
        oEvtListeners.aEls.push(this);  
        oEvtListeners.aEvts.push([fListener]);  
        this["on" + sEventType] = runListeners;  
      } else {  
        var aElListeners = oEvtListeners.aEvts[nElIdx];  
        if (this["on" + sEventType] !== runListeners) {  
          aElListeners.splice(0);  
          this["on" + sEventType] = runListeners;  
        }  
        for (var iLstId = 0; iLstId < aElListeners.length; iLstId++) {  
          if (aElListeners[iLstId] === fListener) { return; }  
        }       
        aElListeners.push(fListener);  
      }  
    } else {  
      oListeners[sEventType] = { aEls: [this], aEvts: [ [fListener] ] };  
      this["on" + sEventType] = runListeners;  
    }  
  };  
  Element.prototype.removeEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {  
    if (!oListeners.hasOwnProperty(sEventType)) { return; }  
    var oEvtListeners = oListeners[sEventType];  
    for (var nElIdx = -1, iElId = 0; iElId < oEvtListeners.aEls.length; iElId++) {  
      if (oEvtListeners.aEls[iElId] === this) { nElIdx = iElId; break; }  
    }  
    if (nElIdx === -1) { return; }  
    for (var iLstId = 0, aElListeners = oEvtListeners.aEvts[nElIdx]; iLstId < aElListeners.length; iLstId++) {  
      if (aElListeners[iLstId] === fListener) { aElListeners.splice(iLstId, 1); }  
    }  
  };  
}  
  • I think what all you need to change is to replace Element.prototype with CustomObject.prototype. And for supporting dispathEvent you must add CustomObject.prototype.dispatchEvent = runListener; code line. Also is can be better to enclose this code into closure function;

I don't tested this in my apps but maybe this can help you.

UPDATE: Next link points into code source that contains XObject() class that supports event adding/removing and dispatching events. Test example is included. All code is based on answer above. http://jsfiddle.net/8jZrR/

like image 34
Andrew D. Avatar answered Oct 08 '22 10:10

Andrew D.