No matter how sure you are about something, if it is not proved, it is not knowledge, only belief or strong belief, and can be wrong. Only what is proved or possible to prove is knowledge.

Implementing a cross-browser event handling system


JavaScript, in the browser, is an event-driven programming language – with no consistent event handling support. While adding and removing listeners might work in almost all JavaScript-enabled browsers, triggering events, to this data, has been a nightmare for many JavaScripters out there and I’m no exception.

While I’ve been working on Hilo a few months ago, I had to deal with this situation. And the thing which I came up with only worked in a few modern browsers. It did not work in Internet Explorer, no wonder. Even after a few days of peeking into other libraries I could not implement a consistent system, whatever the reason might be. Recently I started rewriting Hilo all over again. And as before, I had to deal with those events. As I already had a pretty bad experience implementing an event handling system, this time I wanted to do it the right way and the modern way whatsoever.

I went on and made addEvent and removeEvent methods which are pretty easy to do and then I wrote this code and started at it for more than 60 seconds…


function triggerEvent () {
 
}

I know how to trigger events, but I wanted my handling system to support custom events, which is really hard to. Listening for custom events can be done in the same way as you listen to other events, but there will be a problem when you need to trigger them. While some browsers provide support for Custom Events, some do not. To handle this situation I came up with an idea:

Store all the events somewhere and call them when that event is fired on that element. GREAT IDEA! But.. I wanted something more.. support for multiple events.

After scratching my head for more than ten minutes, I came up with my implementation of cross-browser event delegation system.

For the sake of this article I’ve made a gist and made it available at https://gist.github.com/erikroyall/6618740.


// Evento - v1.0.0
// by Erik Royall  (http://erikroyall.github.io)
// Dual licensed under MIT and GPL
 
// Array.prototype.indexOf shim
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
 
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
    'use strict';
    if (this == null) {
      throw new TypeError();
    }
    
    var n, k, t = Object(this)
      , len = t.length >>> 0;
 
    if (len === 0) {
      return -1;
    }
    
    n = 0;
    if (arguments.length > 1) {
      n = Number(arguments[1]);
      if (n != n) { // shortcut for verifying if it's NaN
        n = 0;
      } else if (n != 0 && n != Infinity && n != -Infinity) {
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
      }
    }
    if (n >= len) {
      return -1;
    }
    for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) {
      if (k in t && t[k] === searchElement) {
        return k;
      }
    }
    return -1;
  };
}
 
var evento = (function (window) {
  var win = window
    , doc = win.document
    , _handlers = {}
    , addEvent
    , removeEvent
    , triggerEvent;
  
  addEvent = (function () {
    if (typeof doc.addEventListener === "function") {
      return function (el, evt, fn) {
        el.addEventListener(evt, fn, false);
        _handlers[el] = _handlers[el] || {};
        _handlers[el][evt] = _handlers[el][evt] || [];
        _handlers[el][evt].push(fn);

      };
    } else if (typeof doc.attachEvent === "function") {
      return function (el, evt, fn) {
        el.attachEvent(evt, fn);
        _handlers[el] = _handlers[el] || {};
        _handlers[el][evt] = _handlers[el][evt] || [];
        _handlers[el][evt].push(fn);
      };
    } else {
      return function (el, evt, fn) {
        el["on" + evt] = fn;
        _handlers[el] = _handlers[el] || {};
        _handlers[el][evt] = _handlers[el][evt] || [];
        _handlers[el][evt].push(fn);
      };
    }
  }());

  // removeEvent
  removeEvent = (function () {
    if (typeof doc.removeEventListener === "function") {
      return function (el, evt, fn) {
        el.removeEventListener(evt, fn, false);
        Helio.each(_handlers[el][evt], function (fun) {
          if (fun === fn) {
            _handlers[el] = _handlers[el] || {};
            _handlers[el][evt] = _handlers[el][evt] || [];
            _handlers[el][evt][_handlers[el][evt].indexOf(fun)] = undefined;
          }
        });

      };
    } else if (typeof doc.detachEvent === "function") {
      return function (el, evt, fn) {
        el.detachEvent(evt, fn);
        Helio.each(_handlers[el][evt], function (fun) {
          if (fun === fn) {
            _handlers[el] = _handlers[el] || {};
            _handlers[el][evt] = _handlers[el][evt] || [];
            _handlers[el][evt][_handlers[el][evt].indexOf(fun)] = undefined;
          }
        });
      };
    } else {
      return function (el, evt, fn) {
        el["on" + evt] = undefined;
        Helio.each(_handlers[el][evt], function (fun) {
          if (fun === fn) {
            _handlers[el] = _handlers[el] || {};
            _handlers[el][evt] = _handlers[el][evt] || [];
            _handlers[el][evt][_handlers[el][evt].indexOf(fun)] = undefined;
          }
        });
      };
    }
  }());

  // triggerEvent
  triggerEvent = function (el, evt) {
    _handlers[el] = _handlers[el] || {};
    _handlers[el][evt] = _handlers[el][evt] || [];

    for (var _i = 0, _l = _handlers[el][evt].length; _i < _l; _i += 1) {
      _handlers[el][evt][_i]();
    }
  };
  
  return {
    add: addEvent,
    remove: removeEvent,
    trigger: triggerEvent,
    _handlers: _handlers
  };
}(this));

I used callbacks in this article to keep it simple. I’ll write another article which purely uses events (fake events) to do this. Thanks for reading tis article. Any suggestions go below…

Why do I keep learning new things always?


Everything looks amazing when we don’t understand them. Once we do, then it seems normal. This happens to everyone in everything.

When I was young, 3 years ago, I did not know how websites can be created. So, I learnt Web Design. I did not know how dynamic websites were created. I learnt PHP and MySQL.

I did not know how applications or programs were made. I downloaded Visual Basic and made my first Hello World. I don’t know how Operating Systems were made. A little googling resulted in SuseStudio.

I heard a lot about C, C#, C++ and wanted to learn them. At last I could manage to learn C++. And C, C# later.

I thought PHP was so slow for my requirements so I shifted to Node.js.

I thought creating my own programming language would be the best option rather than using someone else’s. But I have not achieved that goal yet.

As I am an Atheist, I need to have a good knowledge of everything. I already know that they cannot be achieved by going to school. So I learnt all I need at About.com.

I want to make product videos on my own so I learnt how to shoot videos and edit them.

I want to design the logos on my own, so I learnt Photoshop. I needed graphics for my websites so I learnt Illustrator.

Now, I need to make more of Web Technologies, especially WebSockets, so I am learning HTML5, a little seriously.

Now, I am scared that I may become Jack of all Master of none. So, I am applying for a Microsoft Certification Exam along with Training.

This story of my learning is a never-ending one. But you should know about it. Thanks for reading. Tell me if you have a similar story like this.