Moving a script? IE might hit you twice!

Tags:

I have seen a lot of strange browser bugs during the years, but I think this one is one of the strangest. This bug is related to IE6 and IE7 only and occurs when a script tag linking to an external JavaScript file is moved on a page and the content of the external JavaScript file is of a certain nature.

What is happening?

First I would like to say a little bit about how this issue arose. Large scale web sites normally use techniques to make pages look like they load faster than they actually do. One technique is to give content a loading order by placing important content in its natural place in a page and less important content at the bottom of the page and then move the less important content into its position of the page. This technique is very commonly used to handle ads delivered by third party advertisement delivery companies where the servers are external and the control over the content delivered is limited.

This technique consists mainly of a placeholder, in form of a <div> tag with an id, where the content should recede on the page and a small script in charge of moving this content from where its included in the page to the placeholder.

In the markup it can look like this:


<!-- "Target" placeholder for where "source" will be moved to --> 
<div id="target"> </div> 
   
<div>A lot more content....</div>

<!-- The "source" which will be moved to the "target" placeholder -->
<div id="source"> 
    <b>Some content we want to move...</b>
</div> 

<!-- A call to the JavaScript moving the "source" to the "target" in the page. --> 
<script type="text/javascript"> 
    moveElement('source', 'target'); 
</script>

Then a simple JavaScript will do the job of moving the content:


function moveElement(elementMoveFrom, elementMoveTo) { 
    var newPos = document.getElementById(elementMoveTo); 
    var oldPos = document.getElementById(elementMoveFrom); 
    if (newPos && oldPos) { 
        var body = oldPos.parentNode; 
        body.removeChild (oldPos);
        newPos.innerHTML='';
        newPos.appendChild (oldPos);
    } 
}

The problem starts if the content we want to move contains a call to an external JavaScript file like this. In other words, if we would like to move something like this it might be a problem:


<div id="source"> 
    <script type="text/javascript" 
                src="http://files.trygve-lie.com/examples/move_script/hello_write.js"> 
    </script> 
</div>

If the external JavaScript file linked to from a script tag is of a certain nature; IE6 and IE7 will ask for the external JavaScript file twice! The external JavaScript file will be requested for when its included on the page. Then it will be asked for once more when its moved to its final position on the page. The good part; the JavaScript will not be executed more than once.

Running the example above in IE6 and IE7 will cause the browser to hit the server twice:

Server log showing IE hitting server twice

When executed with other browsers such as Opera, FireFox and Safari or the newer IE8, the server will be hit only once.

Nature of the external script causing the bug

The external JavaScript must be of a certain nature for this to happen. It looks like methods which interact directly with the document will trigger the bug. Methods such as write(); and alert(); in the external JavaScript are known methods which will cause the second hit when the script tag is moved.

Do note that this will only happen if the external JavaScript file is referred to with an absolute URL in the script tag. This bug does not occur if the URL to the JavaScript is relative (that makes sense or what....??)

Why is this a problem?

This might look like a non harmful bug, however it leads to the server serving the external JavaScript file having a much higher hit load than necessary. If this JavaScript file is generated dynamically it will definitely be a bug worth getting rid off to reduce the load on the server.

This bug is especially crucial for advertisement delivery solutions that are based on serving ads as JavaScripts where the JavaScripts consist of write() methods writing markup (the ad) directly to the document. In this case, the advertisement server will have a double hit for each ad served even if the ad is only displayed once to the user. Your advertiser will not be very happy about that...

How to fix it

There are several solutions to this problem. The best solution however is to rewrite the external JavaScript file so it does not use any methods which interact directly with the document. For example, instead of using write(); the following can be used:


var source = document.getElementById('source');
source.innerHTML='Hello world from the hello_dom.js file!'

If the external JavaScript file which is moved is located on a server one cannot do changes on, one simple solution is to replace the value of the src attribute on the script tag with an "empty" value just before the move. This way the external JavaScript file will be loaded and executed when included at the bottom, but when moved the second request will be for the "empty" value found in the src attribute. In other words; the JavaScript file which gets the second request is removed before the second request happens.

The JavaScript used to move the content will then be as follows:


function moveElement(elementMoveFrom, elementMoveTo) { 
    var newPos = document.getElementById(elementMoveTo); 
    var oldPos = document.getElementById(elementMoveFrom); 
    if (newPos && oldPos) { 


        var x=oldPos.getElementsByTagName('script'); 
        if (x !== null) { 
            for (i = 0; i < x.length; i++) { 
                var t = x[i]; 
                t.src = t.src.replace(t.src, '#'); 
            } 
        } 

        var body = oldPos.parentNode; 
        body.removeChild (oldPos);
        newPos.innerHTML='';
        newPos.appendChild (oldPos);
    } 
}

The above JavaScript will loop trough all script tags and replace the src attributes of any script tags found with an "empty" value (marked with red) before moving the content.

How to detect this bug?

As mentioned the nature of the external JavaScript file that is moved is playing a role here. It also looks like the second move does not execute the JavaScript that is moved. And, since "the bug" only happens in IE6 and IE7 it becomes hard to discover with ordinary development tools. The best way to discover this bug is actually to take a look in the web-servers access log and check if a script are requested twice. Example of double hit in an access log:

Server log showing IE hitting server twice

If you are dealing with an external script located on a server you do not have access to, try to download the external script to a server you have access to and change the markup so it points to the local copy so you can see what is going on in the access log.

Comments:

Nice article! I ran at the same problem recently, how I'm using the jquery library it has a function ($.getScript) that helped a lot. Take a look at one examle here: http://stackoverflow.com/questions/1325837/dynamic-including-javascript-with-jquery

Post a Comment:

HTML Syntax: NOT allowed