It should now be universally accepted that extending the DOM directly (via Element.prototype, Node.prototype etc.) is a bad idea. To combat the inherent problems with doing this, quite a few libraries (jQuery, BBC Glow, etc.) employ the “wrapper” technique which involves taking a bunch of DOM elements and stuffing them into an array-like object that inherits from a safely-extendible prototype.
If you want to have a go at creating your own jQuery-like DOM library, here’s something to get you started:
window.myWrapper = (function(){ // "cache" useful methods, so they don't // have to be resolved every time they're used var push = Array.prototype.push, slice = Array.prototype.slice, toString = Object.prototype.toString, isArray = function(o) { toString.call(o) === '[object Array]'; }, toArray = (function(){ try { // Return a basic slice() if the environment // is okay with converting NodeLists to // arrays using slice() slice.call(document.childNodes); return function(arrayLike) { return slice.call(arrayLike); }; } catch(e) {} // Otherwise return the slower approach return function(arrayLike) { var ret = [], i = -1, len = arrayLike.length; while (++i < len) { ret[i] = arrayLike[i]; } return ret; }; })(); function NodeList(elems) { this.length = 0; push.apply( this, elems.nodeType ? [elems] // Single node : isArray(elems) ? elems // Real array : toArray(elems) // NodeList/Array-like-object ); } function myWrapper(elems) { // Instantiate and return new NodeList return new NodeList(elems); } myWrapper.NodeList = NodeList; NodeList.prototype = { each: function(fn) { for (var i = -1, l = this.length; ++i < l;) { fn.call(this[i], this[i], i, l, this); } return this; } }; return myWrapper; })();
There’s no selector engine, but it’s easy enough to slot one in:
function myWrapper(elems) { // E.g. using Sizzle as the selector engine return new NodeList( typeof elems === 'string' ? Sizzle(elems) : elems ); }
Example of usage:
myWrapper(document.getElementsByTagName('div')).each(function(){ alert(this.id); }); // Etc.
Extending NodeList:
myWrapper.NodeList.prototype.applyCSS = (function(){ var hasOwn = Object.prototype.hasOwnProperty; return function(props) { for (var i = -1, l = this.length; ++i < l;) { for (var p in props) { if (hasOwn.call(props, p)) { this[i].style[p] = props[p]; } } } return this; }; }()); // Usage: myWrapper(document.body).applyCSS({ background: 'red', color: 'yellow' });
