My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Wednesday, September 20, 2006

anonymous function to add simple events

This post shows as is possible to set prototypes with anonymous functions.
This one shows a simple anonymous function application example, a fake addEventListener.
We often use directly the method with the document element, it's simple and fast, then often our preferred way to implement an event.
I'm talking about this code

document.getElementById("myId").onclick =
function(){alert("hello")};


DOM and standars like this method to add an event

document.getElementById("myId").addEventListener(
"click",
function(){alert("hello")},
false
);


But IE doesn't implement addEventListener method (what a news ...)

document.getElementById("myId").attachEvent(
"onclick",
function(){alert("hello")}
);


The attachEvent has different problems, it isn't a standard method (then IE and few other browsers supports that) and the scope inside the callback is not the element.
This code, for example, doesn't work as expected:

document.getElementById("myId").attachEvent(
"onclick",
function(){alert(this.className)}
);


I've implemented the addEventListener in my big dollar function but often developers doesn't like "big" external libraries (scriptacolous as prototype and Dojo are some exceptions).
That's why I'm writing this simple function to add an event directly to an element, using generic on* event names.

function addSimpleEvent(
obj, // the object (i.e. window, document, element)
type, // the type (i.e. "onload", "onmouseover", "onclick")
callback // the callback (i.e. function(){alert(this)})
) {
obj[type] = (function(base){ // anonymous function
return function(evt){ // function called on event
if(!evt)evt=window.event; // event for IE browsers
if(base)base.call(this,evt); // old function, if defined
callback.call(this,evt); // callback
}
})(obj[type]) // old defined (or not) function
};

This function uses anonymous function to preserve old event (base variable), if presents, and calls every callback with the element scope (using call).
This is an example:

// imagine that other script did it ...
onload = function(){alert("Hello 1")};

// addSimpleEvent function
function addSimpleEvent(obj,type,callback) {
obj[type] = (function(base){return function(evt){
if(!evt)evt=window.event;
if(base)base.call(this,evt);
callback.call(this,evt);
}})(obj[type])
};

// you can add one, two or more events
addSimpleEvent(window, "onload", function(){alert("Hello 2")});
addSimpleEvent(window, "onload", function(){alert("Hello 3")});


// if you want, you could create another function to add multiple events of same type
function addMultipleEvents() {
for(var i = 2, j = arguments.length; i < j; i++)
addSimpleEvent(arguments[0], arguments[1], arguments[i]);
}

// and use it in this way
addMultipleEvents(window, "onload",
function(){alert("Hello 4")},
function(){alert("Hello 5")},
function(){alert("Hello 6")}
);

Just test this script to view this sequence of alerts

Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6

What's about compatibility ? Every browser that supports call and doesn't loose base variable during execution, then IE 5.5+, FireFox, Opera, KDE, Safari and many others.

I hope this little function will be usefull for you window, document, or element common events.

No comments: