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

Saturday, September 16, 2006

Big dollar $ function to solve standards

Maybe the best and elegant way to get one or more document element as you know.

The $ function is used from a lot of JS developers, it's simple, fast and allow us to reduce scripts size (forget document.getElementById).

Then that's my idea: why we can't use $ to add element standards ?

Every element that's grabbed with $ function should be parsed by function to improve Element standards ... and why not, to add special features for each element.

Here there's an example: big $ function where you can view my first standard implementation, addEventListener and removeEventListener for every browser.

It's not based on attachEvent because attachEvent doesn't works with element scope (this is not the element inside an event listner function ... and it's terrible !!!) and this alpha version manages correctly whellscroll too for IE6, FireFox or Opera 9.

This function will be extended to add a lot of standard Element methods but only if this is a good solution or a good idea, then I'm waiting for some reply :)

Finally, here there's alpha code for a big $ function

function $() {
// (C) Andrea Giammarchi - alpha release
if(!window.$_$)
window.$_$ = {
elementsList: [],
eventsList: {
DOMActivate :"ondomactivate",
DOMAttrModified :"onattrmodified",
DOMCharacterDataModified:"oncharacterdatamodified",
DOMFocusIn :"ondomfocusin",
DOMFocusOut :"ondomfocusout",
DOMMouseScroll :"onmousewheel",
DOMNodeInserted :"onnodeinserted",
DOMSubtreeModified :"onsubtreemodified",
NodeInsertedIntoDocument:"onnodeinsertedintodocument"
},
$A: function(obj) {
var i = obj.length, result = [];
while(i)result.push(obj[--i]);
return result
},
elementsManager: function(element, eventName, callback, listener) {
var i = 0;
if(!this.elementsList.some(function(obj, j){var b = obj.node === element; if(b)i = j; return b}))
i = this.elementsList.push({node:element,events:{}}) - 1;
callback(this.elementsList[i], eventName, listener);
},
attachEvent: function(element, eventName, listener) {
if(!element.events[eventName])
element.events[eventName] = [];
if(element.events[eventName].indexOf(listener) < 0)
element.events[eventName].push(listener);
element.node[eventName] = function(event) {
element.events[eventName].forEach(
function(listener){
if(event) listener.call(element.node, event);
else listener.call(element.node, window.event);
}
)
}
},
detachEvent: function(element, eventName, listener) {
if(element.events[eventName])
element.events[eventName] = element.events[eventName].filter(function(lst){return lst!==listener});
},
temporaryMethod: function(methodName, element, base, eventName, listener, useCapture) {
methodName.push(element[methodName[0]]);
element[methodName[0]] = base;
element[methodName[0]](eventName, listener, !!useCapture);
element[methodName[0]] = methodName.pop();
},
eventsManager: function(addEvent, element, base, eventName, listener, useCapture) {
var methodName = addEvent ? ["addEventListener", "attachEvent"] : ["removeEventListener", "detachEvent"];
if(this.eventsList[eventName]) {
if(base && !window.opera)
this.temporaryMethod(methodName, element, base, eventName, listener, useCapture);
else
this.elementsManager(element, this.eventsList[eventName], this[methodName[1]], listener);
}
else {
if(base)
this.temporaryMethod(methodName, element, base, eventName, listener, useCapture);
else
this.elementsManager(element, "on".concat(eventName), this[methodName[1]], listener);
}
}
};
var elements = window.$_$.$A(arguments);
elements.forEach(function(element, i){
if(element.constructor === String)
elements[i] = document.getElementById(element);
if(window.$_$.elementsList.indexOf(elements[i]) < 0) {
window.$_$.elementsList.push(elements[i]);
elements[i].addEventListener = (function(base) {
return function(eventName, listener, useCapture) {
window.$_$.eventsManager(true, this, base, eventName, listener, useCapture);
}
})(elements[i].addEventListener);
elements[i].removeEventListener = (function(base) {
return function(eventName, listener, useCapture) {
window.$_$.eventsManager(false, this, base, eventName, listener, useCapture);
}
})(elements[i].removeEventListener);
}
});
return elements.length === 1 ? elements[0] : elements
};

2 comments:

Yaroslaff "Inviz" Fedin said...

You're a bit late with this idea, It's and way already implemented to 16th of september in both prototype and mootools with vary. But indeed it's a good idea.

Epper said...

Ehm... Why there is a list of mutation events at the beginning of the script? (DOMNodeInserted ecc...)
Does this script extend the support of these events even to IE? :|