title: Load javascript async, then check DOM loaded before executing callback
body: <p><strong>Problem:</strong><br>
  Load js files asynchronously, then check to see if the dom is loaded before the callback from loading the files is executed.   </p>
  
  <p><strong>edit:</strong> We do not use jQuery; we use Prototype.<br>
  <strong>edit:</strong> added more comments to the code example. </p>
  
  <p>I am trying to load all of my js files asynchronously so as to keep them from blocking the rest of the page. But when the scripts load and the callback is called, I need to know if the DOM has been loaded or not, so I know how to structure the callback. See below:</p>
  
  <pre><code>//load asynchronously
  (function(){
          var e = document.createElement('script'); 
          e.type = "text/javascript";
          e.async = true;
          e.src = srcstr; 
          // a little magic to make the callback happen
          if(navigator.userAgent.indexOf("Opera")){
              e.text = "initPage();";
          }else if(navigator.userAgent.indexOf("MSIE")){
              e.onreadystatechange = initPage;
          }else{
              e.innerHTML = "initPage();";
          }
          // attach the file to the document
          document.getElementsByTagName('head')[0].appendChild(e);
  })();
  
  initPageHelper = function(){ 
      //requires DOM be loaded
  }
  
  initPage = function(){
      if(domLoaded){ // if dom is already loaded, just call the function
          initPageHelper();
      }else{ //if dom is not loaded, attach the function to be run when it does load
          document.observe("dom:loaded", initPageHelper);
      }
  }
  </code></pre>
  
  <p>The callback gets called properly due to some magic behind the scenes that you can learn about from this Google talk: <a href="http://www.youtube.com/watch?v=52gL93S3usU&amp;feature=related" rel="nofollow noreferrer">http://www.youtube.com/watch?v=52gL93S3usU&amp;feature=related</a></p>
  
  <p>What's the easiest, cross-browser method for asking if the DOM has loaded already?</p>
  
  <p><strong>EDIT</strong><br>
  Here's the full solution I went with.<br>
  I included prototype and the asynchronous script loader using the normal method. Life is just so much easier with prototype, so I'm willing to block for that script. </p>
  
  <pre><code>&lt;script type="text/javascript" src="prototype/prototype.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="asyncLoader.js"&gt;&lt;/script&gt;
  </code></pre>
  
  <p>And actually, in my code I minified the two files above and put them together into one file to minimize transfer time and http requests.  </p>
  
  <p>Then I define what I want to run when the DOM loads, and then call the function to load the other scripts. </p>
  
  <pre><code>&lt;script type="text/javascript"&gt;
      initPage = function(){
      ...
      }
  &lt;/script&gt;
  &lt;script type="text/javascript"&gt;
      loadScriptAsync("scriptaculous/scriptaculous.js", initPage);
      loadScriptAsync("scriptaculous/effects.js", initPage);
      loadScriptAsync("scriptaculous/controls.js", initPage);
          ...
      loadScriptAsync("mypage.js", initPage);
  &lt;/script&gt;
  </code></pre>
  
  <p>Likewise, the requests above are actually compressed into one httpRequest using a minifier. They are left separate here for readability. There is a snippet at the bottom of this post showing what the code looks like with the minifier. </p>
  
  <p>The code for asyncLoader.js is the following:</p>
  
  <pre><code>/**
   * Allows you to load js files asynchronously, with a callback that can be 
   * called immediately after the script loads, OR after the script loads and 
   * after the DOM is loaded. 
   * 
   * Prototype.js must be loaded first. 
   * 
   * For best results, create a regular script tag that calls a minified, combined
   * file that contains Prototype.js, and this file. Then all subsequent scripts
   * should be loaded using this function. 
   * 
   */
  var onload_queue = [];
  var dom_loaded = false;
  function loadScriptAsync(src, callback, run_immediately) {
        var script = document.createElement('script'); 
        script.type = "text/javascript";
        script.async = true;
        script.src = src;
        if("undefined" != typeof callback){
            script.onload = function() {
                  if (dom_loaded || run_immediately) 
                    callback();
                  else 
                    onload_queue.push(callback);
                  // clean up for IE and Opera
                  script.onload = null;
                  script.onreadystatechange = null;
            };
  
            script.onreadystatechange = function() {
              if (script.readyState == 'complete'){
                  if (dom_loaded || run_immediately) 
                    callback();
                  else 
                    onload_queue.push(callback);
                  // clean up for IE and Opera
                  script.onload = null;
                  script.onreadystatechange = null;
              }else if(script.readyState == 'loaded'){
                  eval(script);
                   if (dom_loaded || run_immediately) 
                        callback();
                  else 
                    onload_queue.push(callback);
                  // clean up for IE and Opera
                  script.onload = null;
                  script.onreadystatechange = null;
              }
            };
        }
        var head = document.getElementsByTagName('head')[0];
        head.appendChild(script);
  }
  document.observe("dom:loaded", function(){
      dom_loaded = true;
      var len = onload_queue.length;
      for (var i = 0; i &lt; len; i++) {
          onload_queue[i]();
      }
      onload_queue = null;
  });
  </code></pre>
  
  <p>I added the option to run a script immediately, if you have scripts that don't rely on the page DOM being fully loaded.  </p>
  
  <p>The minified requests actually look like:</p>
  
  <pre><code>&lt;script type="text/javascript" src="/min/?b=javascript/lib&amp;f=prototype/prototype.js,asyncLoader.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript"&gt; initPage = function(e){...}&lt;/script&gt;
  &lt;script type="text/javascript"&gt;
      srcstr = "/min/?f=&lt;?=implode(',', $js_files)?&gt;";
      loadScriptAsync(srcstr, initPage);
   &lt;/script&gt;
  </code></pre>
  
  <p>They are using the plugin from: [<a href="http://code.google.com/p/minify/][1]" rel="nofollow noreferrer">http://code.google.com/p/minify/][1]</a></p>