Why are JavaScript calls a problem

As the web evolves and more and more sites move towards being “web 2.0” applications, JavaScript is used more and in greater volume. This is a major problem because most browsers including IE and Firefox block loading and rendering elements below script tags, until the script has been downloaded and executed. Steve Sounders wrote a killer blog about preventing blocking of JavaScript calls, but he didn’t address the situation where scripts call document.write() to inject HTML immediately below the script tag. This test page shows that using the Script DOM Element technique causes document.write calls from within the external script to output html at undesirable places. This blog introduces a new technique called “postload dummy replacement”, that specifically addresses loading scripts that rely on document.write().

Who uses External JavaScript

You can pick up external JavaScript from many different sources. It is commonly used by ad agencies, widgets, and third party embeddable objects. For instance, a VeriSign logo that let’s people know your site is secure and a Skype button I recently picked up to allow visitors to see when I’m online and skype me, both use externally loaded JavaScript that rely on document.write(). Ad Agencies can even nest external JavaScript within external JavaScript leaving you at the mercy of multiple third party providers.

Fighting JavaScript with JavaScript

Ever heard the expression fight fire with fire. Well, for this web optimization technique, we will be fighting JavaScript with JavaScript. This technique I call “postload dummy replacement” allows you to load these JavaScript files inside a hidden DIV at the very bottom of your web page, then use JavaScript to swap out the the contents created by document.write calls inside the hidden DIV with contents of a placeholder element where the JavaScript was originally located.

GetSnappy.com Ad Delay

Ad Delay, is a JavaScript module I’ve written that implements “postload dummy replacement”. I’ll introduce you to Ad Delay, by walking through an example. I’ll use the Skype button mentioned above in my example. We’ll start by creating a placeholder DIV and set it’s id to “skypePlaceholder”.

<div><div id="skypeUsPlaceholder"></div></div>

While “skypeUsPlaceholder” is empty, you are free to implement placeholders however you please, and there contents will be replaced by the call to adDelay.swapOut() mentioned in the last step. You might want to set a height and width for the placeholder, to prevent the page from “jumping” when the swapOut occurs, and you could even include an Ajax loading icon, such as the ones available from http://www.ajaxload.info/.

Now let’s jump down to the bottom of our web page, immediately before the closing </body> tag. Here we will include the JavaScript tag within a “hidden” dummy DIV. We will assign this DIV the id “skypeDummy”. When the script executes document.write() calls fill the “hidden” DIV with the intended content.

<div id="skypeUsDummy" style="display: none;">
  <!--
  Externally loaded JavaScript code
  -->
  <script type="text/javascript"
          src="http://download.skype.com/share/skypebuttons/js/skypeCheck.js">
  </script>
  <a href="skype:GetSnappy.com?chat">
  <img src="http://download.skype.com/share/skypebuttons/buttons/chat_green_white_164x52.png"
       style="border: none;" width="164" height="52" alt="Chat with me" />
  </a>
</div>

Next we’ll unveil the source for Ad Delay which should be included at the very bottom of your web page. Ad Delay is a tiny JavaScript module containing one function, adDelay.swapOut.

/*
 * Name: Ad Delay v0.1 
 * License:  GNUv2 (www.gnu.org/licenses/gpl-2.0.txt)
 * Auther: Brian Gardner <getsnappy@ymail.com>
 * Usage:
 *
 * adDelay.swapOut(arg1, arg2)
 *     arg1 - the id of the placeholder div (in our example, skypePlaceholder)
 *     arg2 - the id of the dummy div (in our example, skypeDummy) 
 *
 */
var adDelay = {
  swapOut: function(placeholderId,dummyId) {
    var dummy = document.getElementById(dummyId);
    var placeholder = document.getElementById(placeholderId);
    if (!dummy) {
      alert("element with id " + dummyId + " does not exist");
    }
    if (!placeholder) {
      alert("element with id " + placeholderId + " does not exist");
    }

    /* loop through dummy nodes removing any JS nodes, preventing double execution */
    for (index = dummy.childNodes.length - 1; index >= 0; index--)
    {
      var element = dummy.childNodes[index];
      if ((element.nodeName == 'SCRIPT') || (element.nodeName == 'NOSCRIPT'))
              dummy.removeChild(element);
    }

    dummy.parentNode.removeChild(dummy);
    placeholder.parentNode.replaceChild(dummy, placeholder);
    dummy.style.display = 'block';
  }
};

After we’ve included the source to Ad Delay, we call adDelay.swapOut to perform the swapOut. Your page now loads faster thanks to Ad Delay : = ).

<script type="text/javascript">
adDelay.swapOut('skypeUsPlaceholder','skypeUsDummy');
</script>