Skip to content

Instantly share code, notes, and snippets.

@JeffreyWay
Created January 18, 2012 21:39
Show Gist options
  • Save JeffreyWay/1635889 to your computer and use it in GitHub Desktop.
Save JeffreyWay/1635889 to your computer and use it in GitHub Desktop.
Legacy JS
// THE GOAL
// Write $('ul').on('click', 'a', fn); in JavaScript
// Must support old IE (so no Selectors API (matchesSelector) or anything)
// Can you shorten this?
var addEvent = (function () {
if (window.addEventListener) {
return function (el, ev, fn) {
el.addEventListener(ev, fn, false);
};
} else {
return function (el, ev, fn) {
el.attachEvent('on' + ev, function() {
return fn.call(el, window.event);
});
};
}
}());
var uls = document.getElementsByTagName('ul');
for ( var i = 0, len = uls.length; i < len; i++ ) {
addEvent(uls[i], 'click', function(e) {
var target = e.target || e.srcElement;
if ( target && target.nodeName.toLowerCase() === 'a' ) {
// proceed
}
});
}
@JeffreyWay
Copy link
Author

Yeah - it's not an attempt to reproduce jQuery's conveniences. More of a quick, "this is how you would do it with legacy, vanilla JS."

@Couto
Copy link

Couto commented Jan 18, 2012

I don't have IE here to test, but I think that e.target is always granted, so we dont't need that verification, please correct me if i'm wrong... .
We can easily replace the addEvent's if with a ternary operator and we can always bring some local variables to shorten our code a bit more…

(function(w, d) {
    var addEvent = (function(list) {
        return (list) ?
            function(el, ev, fn) {
                el.addEventListener(ev, fn, false);
            } : 
            function(el, ev, fn) {
                el.attachEvent('on' + ev, function() {
                    return fn.call(el, w.event);
                });
            }
    }(d.addEventListener));

    var uls = d.getElementsByTagName('ul'), 
        i = uls.length - 1;

    for (; i >= 0; i--) {
        addEvent(uls[i], 'click', function(e) {
            if (e.target.nodeName === 'A') {
                // proceed
            }
        });
    }
}(window, document));

@dhrrgn
Copy link

dhrrgn commented Jan 18, 2012

Can get a lot shorter written in CoffeeScript :P

addEvent = (->
  if window.addEventListener
    return (el, ev, fn) ->
      el.addEventListener ev, fn, false

  (el, ev, fn) ->
    el.attachEvent "on#{ev}", ->
      fn.call el, window.event
)()

for ul in document.getElementsByTagName 'ul'
  addEvent ul, 'click', (e) ->
    if e.target and e.target.nodeName.toLowerCase() == 'a'
      #proceed

(Note: Compilation will Fail with not logic actually inside the final if statement in the loop)

if you are curious, here is what that compiles into (used the -b switch on compilation)

var addEvent, ul, _i, _len, _ref;
addEvent = (function() {
  if (window.addEventListener) {
    return function(el, ev, fn) {
      return el.addEventListener(ev, fn, false);
    };
  }
  return function(el, ev, fn) {
    return el.attachEvent("on" + ev, function() {
      return fn.call(el, window.event);
    });
  };
})();
_ref = document.getElementsByTagName('ul');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  ul = _ref[_i];
  addEvent(ul, 'click', function(e) {
    if (e.target && e.target.nodeName.toLowerCase() === 'a') {
      // proceed
    }
  });
}

@jwmcpeak
Copy link

The target property doesn't exist in legacy IE's event model. So the handler would need to be modified to take that into account. Quick example:

addEvent(uls[i], 'click', function(e) {
    var target = e.target || e.srcElement;
    if (target.nodeName === 'A') {
        // proceed
    }
});

@eliperelman
Copy link

It's a tad bigger, but the API is nice, should support IE, and chaining on calls: https://gist.github.com/1636094 . Consuming code will benefit and shrink the more you use it as you won't have to fetch the elements and loop and do the attaching every time.

@eliperelman
Copy link

@jwmcpeak Also, e is not available, so you need to normalize that as well:

var evt = e || window.event,
    target = e.target || e.srcElement;

@jwmcpeak
Copy link

@eliperelman Not by default, but the addEvent() implementation above takes care of that.

fn.call(el, window.event);

@eliperelman
Copy link

You are correct. My fork operates differently, so I didn't take that into consideration. Also the variable I commented before is the wrong one. :)

@eliperelman
Copy link

@JeffreyWay
Copy link
Author

@jeremy - Oh yeah, forgot about that.

@mattneary
Copy link

Tweet sized addEvent function:

a=(function(a,b)function(c,d,e)a[b]?c[b].call(c,d,e):c.attachEvent("on"+d,function()e.call(c,a.event)))(window,"addEventListener")

Note that JavaScript 1.8 is required for the function shorthand. Here it is unminified

var addEvent = (function (w,ael) {
    return function (el, ev, fn) {
        w[ael] ? el[ael].call(el, ev, fn) :     
        el.attachEvent('on' + ev, function() {
            return fn.call(el, w.event);
        });
    };
}(window,"addEventListener"));

@heavensloop
Copy link

This might also work fine:

 1 var addEvent = addEV;
 2 function addEV(){
 3     return function (el, ev, fn) {
 4         if (window.addEventListener)
 5             el.addEventListener(ev, fn, false);
 6         else{
 7             el.attachEvent('on' + ev, function() {
 8                 return fn.call(el, window.event);
 9             });
10         }
11      };
12 }
13 var uls = document.getElementsByTagName('ul');
14 
15 for ( var i = 0, len = uls.length; i < len; i++ ) {
16     addEvent(uls[i], 'click', function(e) {
17         if ( e.target && e.target.nodeName.toLowerCase() === 'a' ) {
18             // proceed
19         }
20     });
21 }
22 

@JeffreyWay
Copy link
Author

Or, if we need multiple element event binding...

var addEvent = (function () {
var filter = function(el, type, fn) {
    for ( var i = 0, len = el.length; i < len; i++ ) {
        addEvent(el[i], type, fn);
    }
};
if ( document.addEventListener ) {
    return function (el, type, fn) {
        if ( el && el.nodeName || el === window ) {
            el.addEventListener(type, fn, false);
        } else if (el && el.length) {
            filter(el, type, fn);
        }
    };
}

return function (el, type, fn) {
    if ( el && el.nodeName || el === window ) {
        el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
    } else if ( el && el.length ) {
        filter(el, type, fn);
    }
};

})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment