-
-
Save JeffreyWay/1635889 to your computer and use it in GitHub Desktop.
// 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 | |
} | |
}); | |
} |
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
}
});
}
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
}
});
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.
@jwmcpeak Also, e
is not available, so you need to normalize that as well:
var evt = e || window.event,
target = e.target || e.srcElement;
@eliperelman Not by default, but the addEvent() implementation above takes care of that.
fn.call(el, window.event);
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. :)
@JeffreyWay longer, but worth it: https://gist.github.com/1636094
@jeremy - Oh yeah, forgot about that.
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"));
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
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);
}
};
})();
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…