Related
I have an object that has methods in it. These methods are put into the object inside an anonymous function. It looks like this:
var t = {};
window.document.addEventListener("keydown", function(e) {
t.scroll = function(x, y) {
window.scrollBy(x, y);
};
t.scrollTo = function(x, y) {
window.scrollTo(x, y);
};
});
(there is a lot more code, but this is enough to show the problem)
Now I want to stop the event listener in some cases. Therefore I am trying to do a removeEventListener but I can't figure out how to do this. I have read in other questions that it is not possible to call removeEventListener on anonymous functions, but is this also the case in this situation?
I have a method in t created inside the anonymous function and therefore I thought it was possible. Looks like this:
t.disable = function() {
window.document.removeEventListener("keydown", this, false);
}
Why can't I do this?
Is there any other (good) way to do this?
Bonus info; this only has to work in Safari, hence the missing IE support.
You can name the function passed and use the name in the removeEventListener. as in:
button.addEventListener('click', function eventHandler() {
///this will execute only once
alert('only once!');
this.removeEventListener('click', eventHandler);
});
EDIT:
This will not work if you are working in strict mode ("use strict";)
EDIT 2:
arguments.callee is now deprecated (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee)
I believe that is the point of an anonymous function, it lacks a name or a way to reference it.
If I were you I would just create a named function, or put it in a variable so you have a reference to it.
var t = {};
var handler = function(e) {
t.scroll = function(x, y) {
window.scrollBy(x, y);
};
t.scrollTo = function(x, y) {
window.scrollTo(x, y);
};
};
window.document.addEventListener("keydown", handler);
You can then remove it by
window.document.removeEventListener("keydown", handler);
A version of Otto Nascarella's solution that works in strict mode is:
button.addEventListener('click', function handler() {
///this will execute only once
alert('only once!');
this.removeEventListener('click', handler);
});
in modern browsers you can do the following...
button.addEventListener( 'click', () => {
alert( 'only once!' );
}, { once: true } );
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));
May be several anonymous functions, keydown1
Warning: only works in Chrome Dev Tools & cannot be used in code: link
There's a new way to do this that is supported by the latest versions of most popular browsers with the exception of Safari.
Check caniuse for updated support.
Update: Now also supported by Sefari (version 15^).
We can add an option to addEventListner called signal and assign a signal from an AbortController on which you can later call the abort() method.
Here is an example.
We create an AbortController:
const controller = new AbortController();
Then we create the eventListner and pass in the option signal:
document.addEventListener('scroll',()=>{
// do something
},{signal: controller.signal})
And then to remove the eventListner at a later time, we call:
controller.abort()
This is not ideal as it removes all, but might work for your needs:
z = document.querySelector('video');
z.parentNode.replaceChild(z.cloneNode(1), z);
Cloning a node copies all of its attributes and their values, including
intrinsic (in–line) listeners. It does not copy event listeners added using
addEventListener()
Node.cloneNode()
A not so anonymous option
element.funky = function() {
console.log("Click!");
};
element.funky.type = "click";
element.funky.capt = false;
element.addEventListener(element.funky.type, element.funky, element.funky.capt);
// blah blah blah
element.removeEventListener(element.funky.type, element.funky, element.funky.capt);
Since receiving feedback from Andy (quite right, but as with many examples, I wished to show a contextual expansion of the idea), here's a less complicated exposition:
<script id="konami" type="text/javascript" async>
var konami = {
ptrn: "38,38,40,40,37,39,37,39,66,65",
kl: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
};
document.body.addEventListener( "keyup", function knm ( evt ) {
konami.kl = konami.kl.slice( -9 );
konami.kl.push( evt.keyCode );
if ( konami.ptrn === konami.kl.join() ) {
evt.target.removeEventListener( "keyup", knm, false );
/* Although at this point we wish to remove a listener
we could easily have had multiple "keyup" listeners
each triggering different functions, so we MUST
say which function we no longer wish to trigger
rather than which listener we wish to remove.
Normal scoping will apply to where we can mention this function
and thus, where we can remove the listener set to trigger it. */
document.body.classList.add( "konami" );
}
}, false );
document.body.removeChild( document.getElementById( "konami" ) );
</script>
This allows an effectively anonymous function structure, avoids the use of the practically deprecated callee, and allows easy removal.
Incidentally: The removal of the script element immediately after setting the listener is a cute trick for hiding code one would prefer wasn't starkly obvious to prying eyes (would spoil the surprise ;-)
So the method (more simply) is:
element.addEventListener( action, function name () {
doSomething();
element.removeEventListener( action, name, capture );
}, capture );
To give a more up-to-date approach to this:
//one-time fire
element.addEventListener('mousedown', {
handleEvent: function (evt) {
element.removeEventListener(evt.type, this, false);
}
}, false);
JavaScript: addEventListener
method registers the specified listener on the EventTarget(Element|document|Window) it's called on.
EventTarget.addEventListener(event_type, handler_function, Bubbling|Capturing);
Mouse, Keyboard events Example test in WebConsole:
var keyboard = function(e) {
console.log('Key_Down Code : ' + e.keyCode);
};
var mouseSimple = function(e) {
var element = e.srcElement || e.target;
var tagName = element.tagName || element.relatedTarget;
console.log('Mouse Over TagName : ' + tagName);
};
var mouseComplex = function(e) {
console.log('Mouse Click Code : ' + e.button);
}
window.document.addEventListener('keydown', keyboard, false);
window.document.addEventListener('mouseover', mouseSimple, false);
window.document.addEventListener('click', mouseComplex, false);
removeEventListener
method removes the event listener previously registered with EventTarget.addEventListener().
window.document.removeEventListener('keydown', keyboard, false);
window.document.removeEventListener('mouseover', mouseSimple, false);
window.document.removeEventListener('click', mouseComplex, false);
caniuse
I have stumbled across the same problem and this was the best solution I could get:
/*Adding the event listener (the 'mousemove' event, in this specific case)*/
element.onmousemove = function(event) {
/*do your stuff*/
};
/*Removing the event listener*/
element.onmousemove = null;
Please keep in mind I have only tested this for the window element and for the 'mousemove' event, so there could be some problems with this approach.
Possibly not the best solution in terms of what you are asking. I have still not determined an efficient method for removing anonymous function declared inline with the event listener invocation.
I personally use a variable to store the <target> and declare the function outside of the event listener invocation eg:
const target = document.querySelector('<identifier>');
function myFunc(event) {
function code;
}
target.addEventListener('click', myFunc);
Then to remove the listener:
target.removeEventListener('click', myFunc);
Not the top recommendation you will receive but to remove anonymous functions the only solution I have found useful is to remove then replace the HTML element. I am sure there must be a better vanilla JS method but I haven't seen it yet.
I know this is a fairly old thread, but thought I might put in my two cents for those who find it useful.
The script (apologies about the uncreative method names):
window.Listener = {
_Active: [],
remove: function(attached, on, callback, capture){
for(var i = 0; i < this._Active.length; i++){
var current = this._Active[i];
if(current[0] === attached && current[1] === on && current[2] === callback){
attached.removeEventListener(on, callback, (capture || false));
return this._Active.splice(i, 1);
}
}
}, removeAtIndex(i){
if(this._Active[i]){
var remove = this._Active[i];
var attached = remove[0], on = remove[1], callback = remove[2];
attached.removeEventListener(on, callback, false);
return this._Active.splice(i, 1);
}
}, purge: function(){
for(var i = 0; i < this._Active.length; i++){
var current = this._Active[i];
current[0].removeEventListener(current[1], current[2]);
this._Active.splice(i, 1);
}
}, declare: function(attached, on, callback, capture){
attached.addEventListener(on, callback, (capture || false));
if(this._Active.push([attached, on, callback])){
return this._Active.length - 1;
}
}
};
And you can use it like so:
// declare a new onclick listener attached to the document
var clickListener = Listener.declare(document, "click" function(e){
// on click, remove the listener and log the clicked element
console.log(e.target);
Listener.removeAtIndex(clickListener);
});
// completely remove all active listeners
// (at least, ones declared via the Listener object)
Listener.purge();
// works exactly like removeEventListener
Listener.remove(element, on, callback);
I just experienced similiar problem with copy-protection wordpress plugin. The code was:
function disableSelection(target){
if (typeof target.onselectstart!="undefined") //For IE
target.onselectstart=function(){return false}
else if (typeof target.style.MozUserSelect!="undefined") //For Firefox
target.style.MozUserSelect="none"
else //All other route (For Opera)
target.onmousedown=function(){return false}
target.style.cursor = "default"
}
And then it was initiated by loosely put
<script type="text/javascript">disableSelection(document.body)</script>.
I came around this simply by attaching other annonymous function to this event:
document.body.onselectstart = function() { return true; };
Set anonymous listener:
document.getElementById('ID').addEventListener('click', () => { alert('Hi'); });
Remove anonymous listener:
document.getElementById('ID').removeEventListener('click',getEventListeners(document.getElementById('ID')).click[0].listener)
Using the AbortController, neat and clean
Attaching EventListener
const el = document.getElementById('ID')
const controller = new AbortController;
el.addEventListener('click',() => {
console.log("Clicked")
},{signal: controller.signal})
when you want to remove the event listener
controller.abort()
Another alternative workaround to achieve this is adding an empty event handler and preventing event propagation.
Let's assume you need to remove mouseleave event handler from an element which has #specific-div id, that is added with an anonymous function, and you can't use removeEventListener() since you don't have a function name.
You can add another event handler to that element and use event.stopImmediatePropagation(), for being sure this event handler works before existing ones you should pass the third parameter (useCapture) as true.
The final code should look like the below:
document.getElementById("specific-div")
.addEventListener("mouseleave", function(event) {
event.stopImmediatePropagation()
}, true);
This could help for some specific cases that you can't prefer cloneNode() method.
window.document.onkeydown = function(){};
I have seen many JQuery functions using parameter function parameters. Even though I use them, It feels rather incomplete without knowing how this work in the back end.
as an example lets use the click method :
$("p").click(function(event){
event.preventdefault();
alert("testing 123");
});
In this code, if I used, "this" inside the method, it will give me the "p" element itself.
However, I cannot figure out the "event" parameter get assigned with something.
shouldn't there be some place with a code bit like
var event = blah blah;
so that the event parameter has some values?
I have clicked the actual JQuery file by pressing f12 and it goes to a function like this
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
I cannot see any place of that filling or assigning something to the parameter named "event"
I have the same problem with $.ajax requests as well,
$.ajax({
type: "POST",
url: url,
async: false,
success: function (data) { }
});
It is visible that there is someplace loading the "data" in the "data" parameter, How and where does the actual data get filled up in? when do we load actual data.
I have seen a bit of similar problem.
The declaration happens in the function parameters themselves.
Declaring data, event, or whatever you want to call it inside the function parameters (any word will work), is effectively the var data = ... statement.
In the instance of an event handler, event is passed by the browser to any function latching on to that event. In the case of the ajax call, as #Alec said, that is the data returning from the server.
jQuery is basically a wrapper that returns an object with many methods. Most of them are not that straightforward, if you want to understand more deeply, you don't have many choices except using the console and going through the source code which you can find here: https://code.jquery.com/jquery-1.12.4.js. Ideally, use uncompressed version. For some methods, it can be quite long to get to the bottom of it. The way the Click callback works is hidden pretty deep.
You can find it this way:
In the console, enter $("p").click. You'll get:
ƒ ( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
}
Which comes from here in the source code:
jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu" ).split( " " ),
function( i, name ) {
// Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
} );
So because you have at least an argument, it runs: this.on( name, null, data, fn ), where this is your jQuery object $('p'), name is 'click', and data is your function(event). So onto this.on():
console.log($('p').on);
ƒ ( types, selector, data, fn ) {
return on( this, types, selector, data, fn );
}
Here, function on isn't global, so it's in jQuery's closure, so back to the source code where you can find:
function on( elem, types, selector, data, fn, one ) {
...
where elem is your jQuery object $('p'), types is 'click', selector is null, data is your function(e) and fn is null. This leads to:
elem.each( function() {
jQuery.event.add( this, types, fn, data, selector );
} );
So you can find:
jQuery.event = {
global: {},
add: function( elem, types, handler, data, selector ) {
...
Where you can find an addEventListener :
elem.addEventListener( type, eventHandle, false );
On addEventListener, the callback has the event parameter, which is native javascript. In jQuery, the callback is eventHandle, so let's find this one:
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" &&
( !e || jQuery.event.triggered !== e.type ) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
So it returns the function dispatch, so now the callback is this :
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) , where arguments is e (the original addEventListener Event). So find dispatch:
dispatch: function( event ) {
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event );
...
So what is this event.fix:
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
// Create a writable copy of the event object and normalize some properties
var i, prop, copy,
type = event.type,
originalEvent = event,
fixHook = this.fixHooks[ type ];
In here you find
event = new jQuery.Event( originalEvent );
jQuery.Event = function( src, props ) {
...
Where the event that is passed as a parameter of click is defined. You can test it by adding properties on jQuery.Event.prototype. Like this for example:
jQuery.Event.prototype.prop = 'newProp';
So, to sum up, the event in function(event), is an instance of jQuery.Event.
See
console.log($('p').click);
console.log($('p').on);
console.log(jQuery.Event)
jQuery.Event.prototype.prop = 'test';
$('p').click(function(event){console.log(event.prop)});
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<p>test</p>
For Ajax, it's probably a bit more straightforward, but again, if you want to know exactly, not much else you can do but go through the source code.
In reference to your first question, about the event parameter, the event is your click. It will never be explicitly declared like a normal variable. It is just a parameter, and in your example the click is the event.
In reference to your ajax question, the data parameter is what is coming back from your backend after a successful post. For example, I use ajax calls to send some information from my frontend. My backend then uses that information to send back data to frontend inside that success: function(data), like JSON. JSON would be the data parameter.
Does anyone know the magic required to get jQuery .trigger() to trigger a custom event that's handled by a (not jQuery) native JavaScript event handler?
test = document.querySelectorAll('.test')[0];
test.addEventListener('click', function() {
console.log('click')
});
test.addEventListener('custom', function(ev) {
console.log('custom', ev.detail)
});
// Custom Native -> Native works as expected
test.dispatchEvent(new CustomEvent('custom', {detail: 'detail'})); // -> "custom" "detail"
// Standard jQuery -> Native works as expected
$(test).trigger('click'); // -> "click"
// Custom jQuery -> Native does not work
$(test).trigger('custom'); // -> No log?
$(test).trigger({type: 'custom'}); // -> No log?
codepen.io live example
Edited to add:
A bit more details on my use case. I'm developing a library that relies on custom events but doesn't itself use jQuery. However, I'd like to make the library convenient for those applications that do have jQuery.
Well, after stepping through the jQuery source in a debugger, it looks like there is a solution. Not elegant, but workable. The trick is to add an onxxxx property to the element, where xxxx is the event name. The addition to the code in the question would be:
test.oncustom = function(ev, data) {
// ev is the jQuery Event object
// data is data passed to jQuery `.trigger()`
}
Note that jQuery does not add custom data to, for example, ev.detail, as would be the case for a standard event. Instead it passes custom data as an additional parameter.
My idea is to create a plugin which will serve as a wrapper around trigger function in jquery:
(function($) {
$.fn.extend({
trigger: function(type, data) {
return this.each(function() {
if (typeof type == "string" && type.startsWith("test:")) {
this.dispatchEvent(new window.CustomEvent(type, data));
}else{
jQuery.event.trigger(type, data, this)
}
});
}
});
})(jQuery);
It is slightly modified code from: https://github.com/jquery/jquery/blob/master/src/event/trigger.js#L185
Assuming that you add handler as follows:
test.addEventListener('test:custom', function(ev) {
console.log('test:custom', ev.detail)
});
You can dispatch it by:
$(test).trigger('test:custom', { detail: 'jquery'});
The downside is that you need to prefix all your custom events with some kind of namespace.
JSFiddle
https://learn.jquery.com/events/introduction-to-custom-events/
At the end of the webpage see:
Here is an example of the usage of .on() and .trigger() that uses custom data in both cases:
$( document ).on( "myCustomEvent", {
foo: "bar"
}, function( event, arg1, arg2 ) {
console.log( event.data.foo ); // "bar"
console.log( arg1 ); // "bim"
console.log( arg2 ); // "baz"
});
$( document ).trigger( "myCustomEvent", [ "bim", "baz" ] );
It's not a magic. The problem located in jQuery's resolving procedure on elem[type]. Your test element doesen't have custom handler but instead has a native click handler.
So, your dirty-fix might look such as:
**test.custom = function () {console.log('custom fixed')};**
Please have a look at a code-snippet from jquery-1.7.2.js below:
// Call a native DOM method on the target with the same name name as the event.
// Can't use an .isFunction() check here because IE6/7 fails that test.
// Don't do default actions on window, that's where global variables be (#6170)
// IE<9 dies on focus/blur to hidden element (#1486)
if (ontype && elem[type] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow(elem)) {
// Don't re-trigger an onFOO event when we call its FOO() method
old = elem[ontype];
if (old) {
elem[ontype] = null;
}
// Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
elem[type]();
jQuery.event.triggered = undefined;
if (old) {
elem[ontype] = old;
}
}
Can somebody tell how to "unbind" an anonymous function?
In jQuery it's capable to do that, but how can I implement this Functionality in my own script.
This is the scenario:
The following code attach a onclick event to the Div which have someDivId as ID, now when you click the DIV, it's showing 'clicked!'.
var a = document.getElementById('someDivId');
bindEvent(a,'click',function(){alert('clicked!');});
That's all great, the problem is how to "un-attach" the Function to the DIV if the function is anonymous or how to "un-attach" all attached events to the 'a' Element?
unBind(a,'click'); //Not necessarily the given params, it's just an example.
This is the code for bindEvent Method:
function bindEvent (el,evtType,fn){
if ( el.attachEvent ) {
el['e'+evtType+fn] = fn;
el[evtType+fn] = function(){
fn.call(el,window.event);
}
el.attachEvent( 'on'+evtType, el[evtType+fn] );
} else {
el.addEventListener( evtType, fn, false );
}
}
Finally, and after hours of Test&Errors i have found a solution, maybe it's not the best or most efficient but... IT WORKS! (Tested on IE9, Firefox 12, Chrome 18)
First all I'v create two cross-browser and auxiliary addEvent() and removeEvent() methods. (Idea taken from Jquery's source code!)
HELPERS.removeEvent = document.removeEventListener ?
function( type, handle,el ) {
if ( el.removeEventListener ) {
//W3C Standard
el.removeEventListener( type, handle, true );
}
} :
function( type, handle,el ) {
if ( el.detachEvent ) {
//The IE way
el.detachEvent( 'on'+type, el[type+handle] );
el[type+handle] = null;
}
};
HELPERS.addEvent = document.addEventListener ?
function( type, handle,el ) {
if ( el.addEventListener ) {
//W3C Standard
el.addEventListener( type, handle, true );
}
} :
function( type, handle,el ) {
if ( el.attachEvent ) {
//The IE way
el['e'+type+handle] = handle;
el[type+handle] = function(){
handle.call(el,window.event);
};
el.attachEvent( 'on'+type, el[type+handle] );
}
}
Also we need some kind of 'container' to store the attached events to elements, like this:
HELPERS.EVTS = {};
And finally the two callable and exposed to the users Methods:
The next one to add an Event(event) and associate this Event to a Method (handler) for a specific Element (el).
function bindEvent(event, handler,el) {
if(!(el in HELPERS.EVT)) {
// HELPERS.EVT stores references to nodes
HELPERS.EVT[el] = {};
}
if(!(event in HELPERS.EVT[el])) {
// each entry contains another entry for each event type
HELPERS.EVT[el][event] = [];
}
// capture reference
HELPERS.EVT[el][event].push([handler, true]);
//Finally call the aux. Method
HELPERS.addEvent(event,handler,el);
return;
}
Lastly the method that un-attach every pre-attached events (event) for an specific Element (el)
function removeAllEvent(event,el) {
if(el in HELPERS.EVT) {
var handlers = HELPERS.EVT[el];
if(event in handlers) {
var eventHandlers = handlers[event];
for(var i = eventHandlers.length; i--;) {
var handler = eventHandlers[i];
HELPERS.removeEvent(event,handler[0],el);
}
}
}
return;
}
By the way, to call this methods you must do the following:
Capture a DOM Node
var a = document.getElementById('some_id');
Call the method 'bindEvent()' with the corresponding parameters.
bindEvent('click',function(){alert('say hi');},a);
And to de-attach it:
removeAllEvent('click',a);
That's all, hope will be useful for somebody one day.
Personally (and I know this isn't the "best" way, as it does require me to think about what I'm doing), I like to just use the on* event properties of the element I'm working with.
This has the convenient upside of being able to quickly and easily detach events.
var a = document.getElementById('someDivId');
a.onclick = function() {alert("Clicked!");};
// later...
a.onclick = null;
However, you do have to be careful with this because if you try to add a second event handler it will overwrite the first. Keep that in mind and you should be all fine.
I'm not sure if you can unbind an anonymous function attached via javascript. If possible you can simple remove the element from the DOM and recreate it. This will get rid of any event handlers previously attached.
JavaScript provides no list of event listeners attached to a node.
You can remove all event listeners of a node but using the Node.cloneNode method, see here: https://developer.mozilla.org/En/DOM/Node.cloneNode
This clones the node (obviously) but it does not clone the event listeners attached to it.
You could also just bind empty functions as event listeners:
function noop() {}
bindEvent(myElement, "click", noop);
This is from jquery's source:
jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
if ( elem.detachEvent ) {
elem.detachEvent( "on" + type, handle );
}
};
How do I create a custom event class similar to ActionScript? What I mean by that is a class that I can use to fire off my own events, send the necessary data.
I don't want to use third-party libraries like YUI or jQuery to do it. My goal is to be able to send a event that looks like this:
document.addEventListener("customEvent", eventHandler, false);
function eventHandler(e){
alert(e.para1);
}
document.dispatchEvent(new CustomEvent("customEvent", para1, para2));
Please no third-party library solutions.
A method that worked for me was to call document.createEvent(), init it and dispatch it with window.dispatchEvent().
var event = document.createEvent("Event");
event.initEvent("customEvent", true, true);
event.customData = getYourCustomData();
window.dispatchEvent(event);
I'm a little late to the party here, but was searching for the same thing. I'm not keen on the first answer (above) because it relies upon the document to manage the custom event. This is dangerous because it's global and could potentially conflict with another script should that script coincidentally rely on the same custom event.
The best solution I've found is here:
Nicholas C. Zakas - Custom Events in Javascript
Unfortunately, since javascript doesn't support inheritance keywords, it's a bit messy with prototyping, but it definitely keeps things tidy.
This is straightforward when using DOM elements to broker the events.
Given an element:
var element = document.querySelector('div#rocket');
For a client to subscribe:
element.addEventListener('liftoff', function(e)
{
console.log('We have liftoff!');
});
Then to dispatch/raise/fire the event, use this code:
element.dispatch(new Event('liftoff'));
This by John Resig:
function addEvent( obj, type, fn ) {
if ( obj.attachEvent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
obj.attachEvent( 'on'+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
if ( obj.detachEvent ) {
obj.detachEvent( 'on'+type, obj[type+fn] );
obj[type+fn] = null;
} else
obj.removeEventListener( type, fn, false );
}
More at his blog post at http://ejohn.org/projects/flexible-javascript-events/.
I was just thinking of assigning a supported handler to a new namespace i.e. a reference to a supported event. The code below works (paste it in console of Chrome) but you can write it in a better format, and you should have additional helper methods (that can redefine themselves as well), for xBrowser support, and for sniffing support types (which after you've detected which path to use, you'll have the function redefine itself. I hope what I have below helps.
var de = document.documentElement || document.getElementsByTagName[0];
function all(){ console.log('all') };
var customEventForSomeSpecificElement = function customEventForSomeSpecificElement() {
return ({
customEvent: function() {
if ('onclick' in de ) {
return 'click';
}
},
init: function(){ return this.customEvent(); }
}).init();
}();
de.addEventListener(customEventForSomeSpecificElement, all, false);
It's not so hard actually - there isn't so many event definitions, only three versions. The first one is the corect one (addEventListener), then there's the IE way (attachEvent) and then there's the compatibility way for older browser (element.onevent = function)
So a complete event handling solution would look something like this:
setEvent = function(element, eventName, handler){
if('addEventListener' in element){
//W3
element.addEventListener(eventName,handler,false);
}else if('attachEvent' in elm){
//IE
elm.attachEvent('on'+eventName,handler)
}else{
// compatibility
elm['on'+eventName] = handler;
}
}
and to clear events:
clearEvent = function(element, eventName, handler){
if('removeEventListener' in element){
//W3
element.removeEventListener(eventName,handler,false);
}else if('detachEvent' in elm){
//IE
elm.detachEvent('on'+eventName,handler)
}else{
// compatibility
elm['on'+eventName] = null;
}
}
and an example:
setEvent(document, "click", function(){alert('hello world!');});
clearEvent(document, "click", function(){alert('hello world!');});
This is not really a complete example though since the compatibility handler always overwrites the previous events (it's not appending actions, it's overwriting) so you probably would like to check if a handler is already set and then save it into some temporary variable and fire it inside the event handler function.