Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute javascript after page load is complete [duplicate]

I have an html page with some pre-rendered content and some yet un-rendered content. I want to display the pre-rendered content immediately, and then begin rendering the rest of the content. I am not using jQuery.

See the following snippet. I have tried this various ways, including injecting my script before the closing body tag and providing my script to populate the DOM as a callback to window.onload, document.body.onload, and document.addEventListener('DOMContentLoaded'). In every case, the page does not display the pre-rendered content until the rest of the content is rendered.

<html><head></head>
  <body>
    <header>What it is, my doge?</header>
    <div id="main"></div>
    <script>
      var main = document.getElementById('main');
      for (var i = 0; i < 500; i++)
        main.innerText += new Date();
    </script>
  </body>
</html>

<html><head></head>
  <body>
    <header>What it is, my doge?</header>
    <div id="main"></div>
    <script>
      var main = document.getElementById('main');
      document.body.onload = function() {
        for (var i = 0; i < 500; i++)
          main.innerText += new Date();
      };
    </script>
  </body>
</html>

<html><head></head>
      <body>
        <header>What it is, my doge?</header>
        <div id="main"></div>
        <script>
          var main = document.getElementById('main');
          window.onload = function() {
            for (var i = 0; i < 500; i++)
              main.innerText += new Date();
          };
        </script>
      </body>
    </html>

<html><head></head>
      <body>
        <header>What it is, my doge?</header>
        <div id="main"></div>
        <script>
          var main = document.getElementById('main');
          document.addEventListener('DOMContentLoaded', function() {
            for (var i = 0; i < 500; i++)
              main.innerText += new Date();
          });
        </script>
      </body>
    </html>

One case that has worked is window.setTimeout with 0 timeout. However, this simply defers the function until there is nothing left to do. Is this the best practice, here?

<html><head></head>
<body>
    <header>What it is, my doge?</header>
    <div id="main"></div>
    <script>
      var main = document.getElementById('main');
      window.setTimeout(function() {
        for (var i = 0; i < 500; i++)
          main.innerText += new Date();
      }, 0);
    </script>
</body>
</html>
like image 333
Matthew James Davis Avatar asked Nov 16 '14 06:11

Matthew James Davis


2 Answers

In terms of a best practice, there isn't one. In terms of a good, common, and acceptable practices, there are a handful. You've hit one:

setTimeout(function() { }, 1);

In this case, the function is executed within the browser's minimum timeout period after all other in-line processing ends.

Similarly, if you want to ensure your function runs shortly after some condition is true, use an interval:

var readyCheck = setInterval(function() {
  if (readyCondition) {
    /* do stuff */
    clearInterval(readyCheck);
  }
}, 1);

I've been using a similar, but more generalized solution in my own work. I define a helper function in the header:

var upon = function(test, fn) {
    if (typeof(test) == 'function' && test()) {
        fn();
    } else if (typeof(test) == 'string' && window[test]) {
        fn();
    } else {
        setTimeout(function() { upon(test, fn); }, 50);
    }
}; // upon()

... and I trigger other functionality when dependencies are resolved:

upon(function() { return MyNS.Thingy; }, function() {
  //  stuff that depends on MyNS.Thingy
});

upon(function() { return document.readyState == 'complete';}, function() {
  // stuff that depends on a fully rendered document
});

Or, if you want a more authoritative good practice, follow Google's example. Create an external async script and inject it before your first header script:

var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true;
s.src = '/path/to/script.js';
var header_scripts = document.getElementsByTagName('script')[0];
header_scripts.parentNode.insertBefore(s, header_scripts);

Google's solution theoretically works on all browsers (IE < 10?) to get an external script executing as soon as possible without interfering with document loading.

If you want an authoritative common practice, check the source for jQuery's onready solution.

like image 146
svidgen Avatar answered Nov 13 '22 09:11

svidgen


Depending on your browser requirements you can use the async tag and import your script after content loads. This probably accomplishes the same thing as setTimeout(func, 0), but perhaps it's a little less hacky.

See http://plnkr.co/edit/7DlNWNHnyX5s6UE8AFiU?p=preview

html:

...
<body>
  <h1 id="main">Hello Plunker!</h1>
  <script async src="script.js"></script>
</body>
...

script.js:

for(var i=0; i<500; ++i) {
  document.getElementById('main').innerText += new Date();
}
like image 27
Clint Powell Avatar answered Nov 13 '22 08:11

Clint Powell