How to inherit from the DOM element class
Old Q but there's a better answer than "Do" or "Don't" now that IE6 is mostly defunct. First of all prototyping core ECMA endpoint-inheritance constructors like 'Array' is pretty harmless and useful if you do it properly and test to avoid breaking existing methods. Definitely stay away from Object and think real hard before messing with Function, however.
If you're sharing code between a lot of people/authors, or dealing with DOM uncertainty, however, it's typically better to create adapter/wrapper objects with a new factory method to use in an inheritance-scheme.
In this case I wrote document.createExtEl to create wrapped DOM elements whose accessible properties are all available via prototype.
Using the following, your "superclass" for divs would be HTMLExtDivElement (in this case globally available - ew, but it's just an example). All references to the original HTMLElement instance's available properties live inside the wrapper's prototype. Note: some old IE properties can't be passed as references or even accessed without throwing errors (awesome), which is what the try/catch is for.
You could normalize common properties by adding logic to put missing or standardized properties in right after the loop wraps instance-available properties but I'll leave that to you.
Now for the love of Pete, don't ever use my code to write some cascading 16-times inheritance foolishness and then implement in some ironically popular library we're all forced to deal with or I will hunt you down and loudly quote "Design Patterns" at you while throwing rotten fruit.
//Implementation just like document.createElement()
//document.createExtEl('div').tagName === 'DIV'
document.createExtEl = ( function(){ //returns a function below
var htmlTags = ['div','a'], //... add all the element tags you care to make extendable here
constructorMap = {},
i = htmlTags.length;
while(i--){
thisTag = htmlTags[i].toLowerCase();
constructorMap[ thisTag ] = function(){
var elObj = document.createElement(thisTag),
thisProto = this.constructor.prototype,
constructorName = 'HTMLExt' + thisTag.charAt(0).toUpperCase() + thisTag.slice(1) + 'Element';
alert(constructorName);
window[constructorName] = this.constructor; //adds a global reference you can access the new constructor from.
for(var x in elObj){ try{ thisProto[x] = elObj[x]; } catch(e){} }
}
}
//all of the above executes once and returned function accesses via closure
return function(tagName){
return new constructorMap[tagName.toLowerCase()]();
}
} )()
//Now in the case of a superclass/constructor for div, you could use HTMLExtDivElement globally
It's not a good idea to do this.
First of all, to inherit from DOM element, you need to have access to that element's prototype. The problem is that not all browsers provide access to prototypes of DOM elements. Newer Gecko and WebKit -based clients, for example, expose some of these prototypes as global objects - HTMLDivElement, HTMLElement, Element, Node, etc.
For example, plain DIV element usually has a prototype chain similar to:
HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype
-> Node.prototype -> Object.prototype -> null
You can access any of them and extend or inherit from as desired. But again, even though you can, I strongly advise not to.
When browser doesn't expose these prototypes, you're pretty much out of luck. You can try retrieving them by following constructor
property of DOM element itself -
document.createElement('div').constructor;
- but then there's no guarantee that element has constructor
property (e.g. IE6 doesn't) and even if it does, that this property references "correct" object. If, after all, constructor does reference correct object, there's still no guarantee that this objects is allowed to be augmented at all. The truth is that host objects are allowed to implement completely bizarre behavior and do not even have to follow rules that native JS objects follow (you can find dozens of such examples in real life).
Second reason you want to avoid inheriting from DOM element prototypes is that mechanism of such inheritance is not really specified anywhere; it could be quirky, unpredictable and overall fragile and unreliable.
Yes, you can create a constructor that would initialize objects with proper prototype chain (i.e. having DOM prototype in it):
function MyDivElement(){}
MyDivElement.prototype = HTMLDivElement.prototype;
var myDiv = new MyDivElement();
typeof myDiv.appendChild; // "function"
- but this is as much as it goes, and usefulness of this whole approach becomes limited by having certain methods in prototype and nothing else -
typeof myDivElement.nodeName; // "undefined"
myDivElement.innerHTML = '<span>foo<\/span>';
myDivElement.childNodes; // Error
Until some standard specifies exact mechanism for inheriting from DOM prototypes (and browsers actually implement that mechanism), it's best to leave them alone, and perhaps try alternative approach - e.g. wrapper or decorator patterns rather than prototype one :)