oncontentready

An alternative to simulate oncontentready on Internet Explorer

This article is written for IE developers, which means that the JavaScripts exmaples are basically only tested on my own IE6 & IE7.

It's always better to manipulate the DOM contents of an Element especially when you try to remove or add Nodes to the Element.

If we would like to modify the child nodes of an Element before is fully ready to be modified, then something interesting may happen. For example:

<html> <body> <div id="foo"> 1 . First Line <script> //expect result: //append 3 .Third Line after 2 . Second Line document.getElementById('foo').appendChild( document.createTextNode('3 .Third Line')); </script> 2 . Second Line </div> </body> </html>

The result will look like this:

1 . First Line 3 . Third Line 2 . Second Line

So here's the problem, the text 3.Third Line should comes after the text 2 . Second Line since we use appendChild() to add new Nodes to the Element.

appendChild Method
The appendChild method appends elements to the end of the childNodes collection.

The reason for this problem is very simple, just because that the DOM tree has not loaded the entire HTMLof this Element and it turns out that the text 1.First Line would be considered as the last child of the Element instead of the text 2.Second Line .

Besides, one more annoying thing is that you may crash IE if you have JavaScripts like this:

<html> <body> <div id="foo"> 1 . First Line <div> <script> //expect result: //append 3 .Third Line after 2 . Second Line document.getElementById('foo').appendChild( document.createTextNode('3 .Third Line')); </script> </div> 2 . Second Line </div> </body> </html>

This problematic codes will crash IE from displaying the original HTML contents and prompt a error message "Operation Abort" which is critical enough for anyone to access the web page.

In order to prevent that, it's always better to defer the JavaScript action until the Element is fully parsed.

So how do we knows that the DOM of an Element is fully parsed? One of the interesting way is read the outerHTML of the Element.

<html> <body> <div id="foo"> 1 . First Line <div> <script> var dEl = document.getElementById('foo'); alert(dEl.outerHTML); </script> </div> 2 . Second Line </div> </body> </html>

For Internet Explorer, you shall actually see its outerHTML like this from the alert(dEl.outerHTML) dialog when DIV#foo has not read its close tag yet.

<DIV id=foo> 1 . First Line <DIV> <SCRIPT> var dEl = document.getElementById('foo'); alert(dEl.outerHTML); </SCRIPT> </DIV>

The interesting thing is that the close Tag of the Element is missing at this moment.

Unlike all the other major A-Grade browsers, if you try to read the outerHTML of an Element while it's being fully parsed in runtime, then you might not get the close tag at the end of its outerHTML in Internet Explorer.

To simplify the code, let's ignore the HTML of its childNodes.

<html> <body> <div id="foo"> 1 . First Line <div> <script> var dEl = document.getElementById('foo'); alert(dEl.cloneNode(false).outerHTML); </script> </div> 2 . Second Line </div> </body> </html>

Then we will get

<DIV id=foo>

Ah ha, we have a DIV Tag without close tag here!

To see the difference when the outerHTML is fully loaded, let's try this in IE.

<html> <body> <div id="foo"> 1 . First Line <div> <script> setTimeout( function(){ var dEl = document.getElementById('foo'); alert(dEl.cloneNode(false).outerHTML); },2000);//get outerHTML later </script> </div> 2 . Second Line </div> </body> </html>

Then we will get (for IE)

<DIV id=foo></DIV>

Therefore, it seems like that we may check whether an Element has close tag in its outerHTML periodically to see if this Element is fully parsed and ready for DOM manipulation.

For example, here we have two functions, onElementAvailable() and onElementContentReady(). The test case would like to append a new textNode 3.Third Line after 2.Second Line . We first check if Element#foo is available then check if it's ready periodically. you may see the example codes below and a live test here.

<html> <head> <script> function onElementAvailable( sId , callback ){ if( (typeof(sId) != 'string') || (typeof(callback) != 'function') ) return false; var fn = function(){ var el = document.getElementById(sId); if( el) return callback.call(el); fn = null; return setTimeout(arguments.callee); }; fn(); } function onElementContentReady( sId , callback ){ if( (typeof(sId) != 'string') || (typeof(callback) != 'function') ) return false; var fn = function(){ var el = this; var sTag = el.tagName ; var fn2 = function(){ //check readyState first if(el.readyState != 'complete') return setTimeout(arguments.callee,0); //if close tag is not required, then call callback if(!el.canHaveHTML){ //close tag is not required return callback.call(el); } //check if it has a close tag if(el.cloneNode(false).outerHTML.indexOf( '</' + sTag + '>') === -1 ) { //close tag is not available yet return setTimeout(arguments.callee,0); } //now we have the full outerHTML fn = fn2 = null; //release some memory, if applicable callback.call(el);//call callback }; fn2(); } onElementAvailable( sId , fn ) } function handle_onElementContentReady(){ this.appendChild( document.createTextNode('3.Third Line') ); alert('done'); } //call handle_onElementContentReady() when //Element#foo is ready for DOM modification onElementContentReady('foo' , handle_onElementContentReady ); </script> </head> <body> <div id="foo"> <!--A Javascript file which loads very slow to defer the loading of DIV#foo--> <script src="defer.php"></script> 1.First Line 2.Second Line </div> </body> </html>

So it looks like we have learned somthing interesting here, but how solid or useful will it be if we can normalize this methodology since all the tricks always have their own limitation and cost?

I did another test to see if this rule applies to all the HTML Elements and I realized that there're some tags always carry the close tag within their outerHTML.

The Last (But Certainly Not The Least) important part ff this article is that now I feel much safer to implement CSS expression() on IE in order to fix some wacky CSS issues or attach new behaviors. You may see how the example here and see if how do I apply dynamic style to those Elements.

Have fun.

Hedger Wang

See also