September 17, 2004

ieproto

Emulating Prototyping of DOM Objects in Internet Explorer

In Mozilla all DOM objects are native Javascript objects, which means we can easily use prototypes to add methods and properties to all instances of a DOM class at once. This is very useful as it allows us to easily extend the functionality of DOM objects. Unfortuantly there isn't such an easy way to do this in IE, but we can emulate it by using behaviors and extending the document.createElement method.

To illustrate my method I've written my own getAttribute and setAttribute functions to replace the pair provided in Internet Explorer which fix the problem of having to use className instead of class and htmlFor instead of for. These need to be applied to every single DOM object.

My methods

The two methods I'm implementing are part of the Element interface, so I've created my own Element class to contain them. The functions themselves are very simple and are shown below.

Element = function () {};

Element.prototype.getAttribute = function (attribute) {
    if (attribute == "class") attribute = "className";
    if (attribute == "for") attribute = "htmlFor";
    return this[attribute];
}

Element.prototype.setAttribute = function (attribute, value) {
    if (attribute == "class") attribute = "className";
    if (attribute == "for") attribute = "htmlFor";
    this[attribute] = value;
}

How does it work

There are two places that DOM objects come from:

  1. they're either present in the original document
  2. or they are created using document.createElement

Adding methods and properties to elements created using the document.createElement method is easy; all we have to do is extend this function, do our modifcations and return our new object. Adding methods and properties to the elements in the original document is a little harder, we have to process each one individually, which would involve walking through the whole document. Fortunatly Internet Explorer provides an easy way to extend the functionality of all elements on a page by using DHTML behaviours.

Using Behaviours

DHTML behaviours, like the Mozilla project's XBL, allow you to bind Javascript (hence behaviours) to the DOM elements in a document.

Dynamic HTML (DHTML) behaviors are components that encapsulate specific functionality or behavior on a page. When applied to a standard HTML element on a page, a behavior enhances that element's default behavior.

Behaviours can be bound to elements using either a CSS property or progmatically using Javascript. This allows us to add Javascript methods and variables to all the HTML objects in a document using a CSS wildcard, the universal selector.

The HTC file we need simply adds the two methods, which are just references to the methods we defined above. It would look something like this:

<PUBLIC:COMPONENT>
    <PUBLIC:METHOD NAME="getAttribute"
           INTERNALNAME="_getAttribute" />
    <PUBLIC:METHOD NAME="setAttribute"
           INTERNALNAME="_setAttribute" />

    <script type="text/javascript">
        var el = new Element;
        _getAttribute = el.getAttribute;
        _setAttribute = el.setAttribute;
    </script>
</PUBLIC:COMPONENT>

Extending createElement

To extend the createElement method we first have to store a reference to Internet Explorer's built in method. We then define our own method in place of the default one, calling the built in method to first create the element. After creating the element it's simply a case of adding our Element interface, which includes the two methods I created above, and returning the new new element with our methods attached.

The code to do this is pretty short and is shown below:

var __IEcreateElement = document.createElement;

document.createElement = function (tagName) {
    var element = __IEcreateElement(tagName);

    var interface = new Element;
    for (method in interface)
        element[method] = interface[method];

    return element;
}

Including the Files

Adding the required CSS and Javascript to your documents is quick and painless, thanks to Internet Explorer's conditional comments, which allow authors to add HTML hidden in specially formatted comment blocks. The snippet you'll need will look something like this:

<!--[if IE]>
    <script type="text/javascript" src="attributes.js"></script>
    <style type="text/css">
        * { behavior: url(attributes.htc); }
    </style>
<![endif]-->

Here I've put the Javascript in a file named attributes.js and the HTC is in a file called attributes.htc. The conditional comment ensures that only Internet Explorer downloads these files. This is should now allow us to access the class and for attributes using the getAttribute and setAttribute in all the modern browsers, including IE.

Closure

Well, that's it, it's a pretty simple method which seems to work fairly well.

10 Comments:

Anonymous said...

hey! welcome back man!

nice stuff you have here, i just hope you dont leave us again for such a long time ;)

Sergi - http://meddle.dzygn.com/

28 September, 2004 16:42  
David JH said...

This method is great! I noticed one problem however; it won't apply to elements that are created by setting the innerHTML property of their container.

e.g. if you have something like

var f = document.forms[0];
f.innerHTML = "<input name=\"thetest\" />";
var inputFromInnerHTML = f.elements["thetest"];
inputFromInnerHTML.foo();

even if method foo is defined in behaviours, it won't be applied to inputFromInnerHTML.

Anybody know of a way around this?

09 August, 2005 20:27  
Anonymous said...

Sure, don't use innerHTML. Use the w3c-type functions instead

17 April, 2006 17:11  
Anonymous said...

w3c-type are much slower than innerhtml

10 June, 2006 00:52  
Anonymous said...

anon,

My guess is that you aren't worried about it being slow, my guess is that you are just being lazy and stupid. Cut it out and do it properly.

25 June, 2006 16:40  
Anonymous said...

Give DOM Builder a go: http://www.vivabit.com/bollocks/2006/04/06/introducing-dom-builder

I use it when building elements. It has the righteousness of W3C functions and the ease of use of innerHTML.

18 July, 2006 02:23  
kentaromiura said...

take a look at my solution (based on this) at http://mykenta.blogspot.com/2006/07/standardise-ie-setattribute-part-2.html
;)

02 October, 2006 08:14  
Anonymous said...

"Sure, don't use innerHTML. Use the w3c-type functions instead"

what functions are you talking about ?
can you post any reference here, please ?

04 March, 2007 20:56  
Anonymous said...

DOM functions. Look up DOM reference.

var f = document.forms[0];
var i = document.createElement('INPUT');
i.setAttribute('name','thetest');
var inputFromInnerHTML = f.appendChild(i);
inputFromInnerHTML.foo();

===
If you want a solution less flexible, but even easier try this:

document._createElement = document.createElement;
document.createElement = function(tag)
{
var el=document._createElement(tag);

el.methodYouWantToAdd = function(args) {
//do stuff with "this"
return this;
}

return el;
}

================
///////
//For example:

document._createElement = document.createElement;
document.createElement = function(tag)
{
var t=document._createElement(tag);

t.appendText = function(txt) {
return this.appendChild(document.createTextNode(txt));
}

t.appendTag = function(tag,txt) {
var t=document.createElement(tag);
if(txt){ t.appendText(txt) };
return this.appendChild(t);
}
return t;
}

///////////////
//Use the above as:
var div = document.createElement('DIV');
var h1=div.appendTag('H1','Hello world!');
div.appendText('Here I am');
h1.appendText('!!!');
h1.appendTag('SUP','2');

12 April, 2007 10:27  
lon987 said...

Nice code

Lon987

15 April, 2007 11:45  

Post a Comment

<< Home