Tutorial :$(document).ready equivalent without jQuery



Question:

I have a script that uses $(document).ready, but it doesn't use anything else from jQuery. I'd like to lighten it up by removing the jQuery dependency.

How can I implement my own $(document).ready functionality without using jQuery? I know that using window.onload will not be the same, as window.onload fires after all images, frames, etc. have been loaded.


Solution:1

There is a standards based replacement,DOMContentLoaded that is supported by over 98% of browsers, though not IE8:

document.addEventListener("DOMContentLoaded", function(event) {     //do work  });  

jQuery's native function is much more complicated than just window.onload, as depicted below.

function bindReady(){      if ( readyBound ) return;      readyBound = true;        // Mozilla, Opera and webkit nightlies currently support this event      if ( document.addEventListener ) {          // Use the handy event callback          document.addEventListener( "DOMContentLoaded", function(){              document.removeEventListener( "DOMContentLoaded", arguments.callee, false );              jQuery.ready();          }, false );        // If IE event model is used      } else if ( document.attachEvent ) {          // ensure firing before onload,          // maybe late but safe also for iframes          document.attachEvent("onreadystatechange", function(){              if ( document.readyState === "complete" ) {                  document.detachEvent( "onreadystatechange", arguments.callee );                  jQuery.ready();              }          });            // If IE and not an iframe          // continually check to see if the document is ready          if ( document.documentElement.doScroll && window == window.top ) (function(){              if ( jQuery.isReady ) return;                try {                  // If IE is used, use the trick by Diego Perini                  // http://javascript.nwbox.com/IEContentLoaded/                  document.documentElement.doScroll("left");              } catch( error ) {                  setTimeout( arguments.callee, 0 );                  return;              }                // and execute any waiting functions              jQuery.ready();          })();      }        // A fallback to window.onload, that will always work      jQuery.event.add( window, "load", jQuery.ready );  }  


Solution:2

Edit:

Here is a viable replacement for jQuery ready

function ready(callback){      // in case the document is already rendered      if (document.readyState!='loading') callback();      // modern browsers      else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);      // IE <= 8      else document.attachEvent('onreadystatechange', function(){          if (document.readyState=='complete') callback();      });  }    ready(function(){      // do something  });  

Taken from https://plainjs.com/javascript/events/running-code-when-the-document-is-ready-15/


As the accepted answer was very far from complete, I stitched together a "ready" function like jQuery.ready() based on jQuery 1.6.2 source:

var ready = (function(){        var readyList,          DOMContentLoaded,          class2type = {};          class2type["[object Boolean]"] = "boolean";          class2type["[object Number]"] = "number";          class2type["[object String]"] = "string";          class2type["[object Function]"] = "function";          class2type["[object Array]"] = "array";          class2type["[object Date]"] = "date";          class2type["[object RegExp]"] = "regexp";          class2type["[object Object]"] = "object";        var ReadyObj = {          // Is the DOM ready to be used? Set to true once it occurs.          isReady: false,          // A counter to track how many items to wait for before          // the ready event fires. See #6781          readyWait: 1,          // Hold (or release) the ready event          holdReady: function( hold ) {              if ( hold ) {                  ReadyObj.readyWait++;              } else {                  ReadyObj.ready( true );              }          },          // Handle when the DOM is ready          ready: function( wait ) {              // Either a released hold or an DOMready/load event and not yet ready              if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {                  // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).                  if ( !document.body ) {                      return setTimeout( ReadyObj.ready, 1 );                  }                    // Remember that the DOM is ready                  ReadyObj.isReady = true;                  // If a normal DOM Ready event fired, decrement, and wait if need be                  if ( wait !== true && --ReadyObj.readyWait > 0 ) {                      return;                  }                  // If there are functions bound, to execute                  readyList.resolveWith( document, [ ReadyObj ] );                    // Trigger any bound ready events                  //if ( ReadyObj.fn.trigger ) {                  //    ReadyObj( document ).trigger( "ready" ).unbind( "ready" );                  //}              }          },          bindReady: function() {              if ( readyList ) {                  return;              }              readyList = ReadyObj._Deferred();                // Catch cases where $(document).ready() is called after the              // browser event has already occurred.              if ( document.readyState === "complete" ) {                  // Handle it asynchronously to allow scripts the opportunity to delay ready                  return setTimeout( ReadyObj.ready, 1 );              }                // Mozilla, Opera and webkit nightlies currently support this event              if ( document.addEventListener ) {                  // Use the handy event callback                  document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );                  // A fallback to window.onload, that will always work                  window.addEventListener( "load", ReadyObj.ready, false );                // If IE event model is used              } else if ( document.attachEvent ) {                  // ensure firing before onload,                  // maybe late but safe also for iframes                  document.attachEvent( "onreadystatechange", DOMContentLoaded );                    // A fallback to window.onload, that will always work                  window.attachEvent( "onload", ReadyObj.ready );                    // If IE and not a frame                  // continually check to see if the document is ready                  var toplevel = false;                    try {                      toplevel = window.frameElement == null;                  } catch(e) {}                    if ( document.documentElement.doScroll && toplevel ) {                      doScrollCheck();                  }              }          },          _Deferred: function() {              var // callbacks list                  callbacks = [],                  // stored [ context , args ]                  fired,                  // to avoid firing when already doing so                  firing,                  // flag to know if the deferred has been cancelled                  cancelled,                  // the deferred itself                  deferred  = {                        // done( f1, f2, ...)                      done: function() {                          if ( !cancelled ) {                              var args = arguments,                                  i,                                  length,                                  elem,                                  type,                                  _fired;                              if ( fired ) {                                  _fired = fired;                                  fired = 0;                              }                              for ( i = 0, length = args.length; i < length; i++ ) {                                  elem = args[ i ];                                  type = ReadyObj.type( elem );                                  if ( type === "array" ) {                                      deferred.done.apply( deferred, elem );                                  } else if ( type === "function" ) {                                      callbacks.push( elem );                                  }                              }                              if ( _fired ) {                                  deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );                              }                          }                          return this;                      },                        // resolve with given context and args                      resolveWith: function( context, args ) {                          if ( !cancelled && !fired && !firing ) {                              // make sure args are available (#8421)                              args = args || [];                              firing = 1;                              try {                                  while( callbacks[ 0 ] ) {                                      callbacks.shift().apply( context, args );//shifts a callback, and applies it to document                                  }                              }                              finally {                                  fired = [ context, args ];                                  firing = 0;                              }                          }                          return this;                      },                        // resolve with this as context and given arguments                      resolve: function() {                          deferred.resolveWith( this, arguments );                          return this;                      },                        // Has this deferred been resolved?                      isResolved: function() {                          return !!( firing || fired );                      },                        // Cancel                      cancel: function() {                          cancelled = 1;                          callbacks = [];                          return this;                      }                  };                return deferred;          },          type: function( obj ) {              return obj == null ?                  String( obj ) :                  class2type[ Object.prototype.toString.call(obj) ] || "object";          }      }      // The DOM ready check for Internet Explorer      function doScrollCheck() {          if ( ReadyObj.isReady ) {              return;          }            try {              // If IE is used, use the trick by Diego Perini              // http://javascript.nwbox.com/IEContentLoaded/              document.documentElement.doScroll("left");          } catch(e) {              setTimeout( doScrollCheck, 1 );              return;          }            // and execute any waiting functions          ReadyObj.ready();      }      // Cleanup functions for the document ready method      if ( document.addEventListener ) {          DOMContentLoaded = function() {              document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );              ReadyObj.ready();          };        } else if ( document.attachEvent ) {          DOMContentLoaded = function() {              // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).              if ( document.readyState === "complete" ) {                  document.detachEvent( "onreadystatechange", DOMContentLoaded );                  ReadyObj.ready();              }          };      }      function ready( fn ) {          // Attach the listeners          ReadyObj.bindReady();            var type = ReadyObj.type( fn );            // Add the callback          readyList.done( fn );//readyList is result of _Deferred()      }      return ready;  })();  

How to use:

<script>      ready(function(){          alert('It works!');      });      ready(function(){          alert('Also works!');      });  </script>  

I am not sure how functional this code is, but it worked fine with my superficial tests. This took quite a while, so I hope you and others can benefit from it.

PS.: I suggest compiling it.

Or you can use http://dustindiaz.com/smallest-domready-ever:

function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}  r(function(){/*code to run*/});  

or the native function if you only need to support the new browsers (Unlike jQuery ready, this won't run if you add this after the page has loaded)

document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})  


Solution:3

Three options:

  1. If script is the last tag of the body, the DOM would be ready before script tag executes
  2. When the DOM is ready, "readyState" will change to "complete"
  3. Put everything under 'DOMContentLoaded' event listener

onreadystatechange

  document.onreadystatechange = function () {       if (document.readyState == "complete") {       // document is ready. Do your stuff here     }   }  

Source: MDN

DOMContentLoaded

document.addEventListener('DOMContentLoaded', function() {     console.log('document is ready. I can sleep now');  });  

Concerned about stone age browsers: Go to the jQuery source code and use the ready function. In that case you are not parsing+executing the whole library you're are doing only a very small part of it.


Solution:4

Place your <script>/*JavaScript code*/</script> right before the closing </body> tag.

Admittedly, this might not suit everyone's purposes since it requires changing the HTML file rather than just doing something in the JavaScript file a la document.ready, but still...


Solution:5

Poor man's solution:

var checkLoad = function() {         document.readyState !== "complete" ? setTimeout(checkLoad, 11) : alert("loaded!");     };      checkLoad();    

View Fiddle

Added this one, a bit better I guess, own scope, and non recursive

(function(){      var tId = setInterval(function() {          if (document.readyState == "complete") onComplete()      }, 11);      function onComplete(){          clearInterval(tId);              alert("loaded!");          };  })()  

View Fiddle


Solution:6

I use this:

document.addEventListener("DOMContentLoaded", function(event) {       //Do work  });  

Note: This probably only works with newer browsers, especially these: http://caniuse.com/#feat=domcontentloaded


Solution:7

Really, if you care about Internet Explorer 9+ only, this code would be enough to replace jQuery.ready:

    document.addEventListener("DOMContentLoaded", callback);  

If you worry about Internet Explorer 6 and some really strange and rare browsers, this will work:

domReady: function (callback) {      // Mozilla, Opera and WebKit      if (document.addEventListener) {          document.addEventListener("DOMContentLoaded", callback, false);          // If Internet Explorer, the event model is used      } else if (document.attachEvent) {          document.attachEvent("onreadystatechange", function() {              if (document.readyState === "complete" ) {                  callback();              }          });          // A fallback to window.onload, that will always work      } else {          var oldOnload = window.onload;          window.onload = function () {              oldOnload && oldOnload();              callback();          }      }  },  


Solution:8

This question was asked quite a long time ago. For anyone just seeing this question, there is now a site called "you might not need jquery" which breaks down - by level of IE support required - all the functionality of jquery and provides some alternative, smaller libraries.

IE8 document ready script according to you might not need jquery

function ready(fn) {      if (document.readyState != 'loading')          fn();      else if (document.addEventListener)          document.addEventListener('DOMContentLoaded', fn);      else          document.attachEvent('onreadystatechange', function() {              if (document.readyState != 'loading')                  fn();          });  }  


Solution:9

I was recently using this for a mobile site. This is John Resig's simplified version from "Pro JavaScript Techniques". It depends on addEvent.

var ready = ( function () {    function ready( f ) {      if( ready.done ) return f();        if( ready.timer ) {        ready.ready.push(f);      } else {        addEvent( window, "load", isDOMReady );        ready.ready = [ f ];        ready.timer = setInterval(isDOMReady, 13);      }    };      function isDOMReady() {      if( ready.done ) return false;        if( document && document.getElementsByTagName && document.getElementById && document.body ) {        clearInterval( ready.timer );        ready.timer = null;        for( var i = 0; i < ready.ready.length; i++ ) {          ready.ready[i]();        }        ready.ready = null;        ready.done = true;      }    }      return ready;  })();  


Solution:10

The jQuery answer was pretty useful to me. With a little refactory it fitted my needs well. I hope it helps anybody else.

function onReady ( callback ){      var addListener = document.addEventListener || document.attachEvent,          removeListener =  document.removeEventListener || document.detachEvent          eventName = document.addEventListener ? "DOMContentLoaded" : "onreadystatechange"        addListener.call(document, eventName, function(){          removeListener( eventName, arguments.callee, false )          callback()      }, false )  }  


Solution:11

Cross-browser (old browsers too) and a simple solution:

var docLoaded = setInterval(function () {      if(document.readyState !== "complete") return;      clearInterval(docLoaded);        /*          Your code goes here i.e. init()      */  }, 30);  

Showing alert in jsfiddle


Solution:12

Here is the smallest code snippet to test DOM ready which works across all browsers (even IE 8):

r(function(){      alert('DOM Ready!');  });  function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}  

See this answer.


Solution:13

Just add this to the bottom of your HTML page...

<script>      Your_Function();  </script>  

Because, HTML documents are parsed by top-bottom.


Solution:14

This cross-browser code will call a function once the DOM is ready:

var domReady=function(func){      var scriptText='('+func+')();';      var scriptElement=document.createElement('script');      scriptElement.innerText=scriptText;      document.body.appendChild(scriptElement);  };  

Here's how it works:

  1. The first line of domReady calls the toString method of the function to get a string representation of the function you pass in and wraps it in an expression that immediately calls the function.
  2. The rest of domReady creates a script element with the expression and appends it to the body of the document.
  3. The browser runs script tags appended to body after the DOM is ready.

For example, if you do this: domReady(function(){alert();});, the following will appended to the body element:

 <script>(function (){alert();})();</script>  

Note that this works only for user-defined functions. The following won't work: domReady(alert);


Solution:15

It is worth looking in Rock Solid addEvent() and http://www.braksator.com/how-to-make-your-own-jquery.

Here is the code in case the site goes down

function addEvent(obj, type, fn) {      if (obj.addEventListener) {          obj.addEventListener(type, fn, false);          EventCache.add(obj, type, fn);      }      else if (obj.attachEvent) {          obj["e"+type+fn] = fn;          obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }          obj.attachEvent( "on"+type, obj[type+fn] );          EventCache.add(obj, type, fn);      }      else {          obj["on"+type] = obj["e"+type+fn];      }  }    var EventCache = function(){      var listEvents = [];      return {          listEvents : listEvents,          add : function(node, sEventName, fHandler){              listEvents.push(arguments);          },          flush : function(){              var i, item;              for(i = listEvents.length - 1; i >= 0; i = i - 1){                  item = listEvents[i];                  if(item[0].removeEventListener){                      item[0].removeEventListener(item[1], item[2], item[3]);                  };                  if(item[1].substring(0, 2) != "on"){                      item[1] = "on" + item[1];                  };                  if(item[0].detachEvent){                      item[0].detachEvent(item[1], item[2]);                  };                  item[0][item[1]] = null;              };          }      };  }();    // Usage  addEvent(window, 'unload', EventCache.flush);  addEvent(window, 'load', function(){alert("I'm ready");});  


Solution:16

How about this solution?

// other onload attached earlier  window.onload=function() {     alert('test');  };    tmpPreviousFunction=window.onload ? window.onload : null;    // our onload function  window.onload=function() {     alert('another message');       // execute previous one     if (tmpPreviousFunction) tmpPreviousFunction();  };  


Solution:17

It's always good to use JavaScript equivalents as compared to jQuery. One reason is one fewer library to depend on and they are much faster than the jQuery equivalents.

One fantastic reference for jQuery equivalents is http://youmightnotneedjquery.com/.

As far as your question is concerned, I took the below code from the above link :) Only caveat is it only works with Internet Explorer 9 and later.

function ready(fn) {      if (document.readyState != 'loading') {          fn();      }      else {          document.addEventListener('DOMContentLoaded', fn);      }  }  


Solution:18

We found a quick-and-dirty cross browser implementation of ours that may do the trick for most simple cases with a minimal implementation:

window.onReady = function onReady(fn){      document.body ? fn() : setTimeout(function(){ onReady(fn);},50);  };  


Solution:19

The setTimeout/setInterval solutions presented here will only work in specific circumstances.

The problem shows up especially in older Internet Explorer versions up to 8.

The variables affecting the success of these setTimeout/setInterval solutions are:

1) dynamic or static HTML  2) cached or non cached requests  3) size of the complete HTML document  4) chunked or non chunked transfer encoding  

the original (native Javascript) code solving this specific issue is here:

https://github.com/dperini/ContentLoaded  http://javascript.nwbox.com/ContentLoaded (test)  

this is the code from which the jQuery team have built their implementation.


Solution:20

Here's what I use, it's fast and covers all bases I think; works for everything except IE<9.

(() => { function fn() {      // "On document ready" commands:      console.log(document.readyState);  };      if (document.readyState != 'loading') {fn()}    else {document.addEventListener('DOMContentLoaded', fn)}  })();  

This seems to catch all cases:

  • fires immediately if the DOM is already ready (if the DOM is not "loading", but either "interactive" or "complete")
  • if the DOM is still loading, it sets up an event listener for when the DOM is available (interactive).

The DOMContentLoaded event is available in IE9 and everything else, so I personally think it's OK to use this. Rewrite the arrow function declaration to a regular anonymous function if you're not transpiling your code from ES2015 to ES5.

If you want to wait until all assets are loaded, all images displayed etc then use window.onload instead.


Solution:21

I simply use:

setTimeout(function(){      //reference/manipulate DOM here  });  

And unlike document.addEventListener("DOMContentLoaded" //etc as in the very top answer, it works as far back as IE9 -- http://caniuse.com/#search=DOMContentLoaded only indicates as recently as IE11.

For instance, go to https://netrenderer.com/index.php, choose Internet Explorer 9 from the dropdown, enter https://dexygen.github.io/blog/oct-2017/jekyll/jekyll-categories/liquid-templates/2017/10/22/how-jekyll-builds-site-categories.html and click "Render", and you will see something akin to the screenshot at the bottom of this post.

See the following Javascript code I am using in the header to manipulate the style of the Jekyll "hacker" theme to my liking -- in particular you can reference the if (location.pathname !== rootPath) block to see how I am inserting the Home and Blog Home links, which are being displayed by IE9 per the NetRenderer site.

Interestingly I stumbled upon this setTimeout solution in 2009: Is checking for the readiness of the DOM overkill?, which probably could have been worded slightly better, as I meant by using various frameworks' more complicated approaches.

setTimeout(function() {//delay execution until after dom is parsed      var containerEls = document.getElementsByClassName('container');      var headingEl = containerEls[0].getElementsByTagName('h1')[0];      var headerEl = document.getElementsByTagName('header')[0];      var downloadsSectionEl = document.getElementById('downloads');      var rootPath = "/";      var blogRootPath = "/blog/";        containerEls[0].style.maxWidth = '800px';      containerEls[1].style.maxWidth = '800px';      headingEl.style.margin = '0';      headerEl.style.marginBottom = '7px';      downloadsSectionEl.style.margin = '0';        if (location.pathname !== rootPath) {          downloadsSectionEl.appendChild(generateNavLink('Home', rootPath));          if (location.pathname !== blogRootPath) {              downloadsSectionEl.appendChild(document.createTextNode(' | '));              downloadsSectionEl.appendChild(generateNavLink('Blog Home', blogRootPath));          }      }        function generateNavLink(linkText, hrefPath) {          var navLink = document.createElement('a');          var linkTextNode = document.createTextNode(linkText);          navLink.setAttribute('href', hrefPath);          navLink.appendChild(linkTextNode);          return navLink;      }  });  

dexygen.github.io on IE9


Solution:22

The ready function in jQuery does a number of things. Frankly, I don't see that point of replacing it unless you have amazingly small output from your website. jQuery is a pretty tiny library, and it handles all sorts of cross-browser things you'll need later.

Anyway, there's little point in posting it here, just open up jQuery and look at the bindReady method.

It starts by calling either document.addEventListener("DOMContentLoaded") or document.attachEvent('onreadystatechange') depending on the event model, and goes on from there.


Solution:23

For IE9+:

function ready(fn) {    if (document.readyState != 'loading'){      fn();    } else {      document.addEventListener('DOMContentLoaded', fn);    }  }  


Solution:24

If you are loading jQuery near the bottom of BODY, but are having trouble with code that writes out jQuery(<func>) or jQuery(document).ready(<func>), check out jqShim on Github.

Rather than recreate its own document ready function, it simply holds onto the functions until jQuery is available then proceeds with jQuery as expected. The point of moving jQuery to the bottom of body is to speed up page load, and you can still accomplish it by inlining the jqShim.min.js in the head of your template.

I ended up writing this code to make moving all the scripts in WordPress to the footer, and just this shim code now sits directly in the header.


Solution:25

This approach is the shortest way I can think of.

The solution based on the DOMContentLoaded event only works if the script is loaded before the document, whereas the lazy check suggested here ensures the code is executed always, even in scripts loaded dynamically later on, exactly as the JQuery's document ready.

This code is compatible with all browsers (including some legacy, down to IE6 and Safari for Windows).

(function ready() {      if (!document.body) {setTimeout(ready, 50); return;}      // Document is ready here  })();  


Solution:26

function onDocReady(fn){      (function c(){document.readyState!=="loading"?fn():setTimeout(c,9)})();  }       function onDocLoad(fn){      (function c(){document.readyState==="complete"?fn():setTimeout(c,30)})();  }      

onDocReady provides a callback when the HTML dom is ready to fully access/parse/manipulate.

onDocLoad provides a callback when everything has loaded (images etc)

  • These functions can be called whenever you want.
  • Supports multiple "listeners".
  • Will work in any browser.


Solution:27

If you don't have to support very old browsers, here is a way to do it even when your external script is loaded with async attribute:

HTMLDocument.prototype.ready = new Promise(function(resolve) {     if(document.readyState != "loading")        resolve();     else        document.addEventListener("DOMContentLoaded", function() {           resolve();        });  });    document.ready.then(function() {     console.log("document.ready");  });  


Solution:28

This was a good https://stackoverflow.com/a/11810957/185565 poor man's solution. One comment considered a counter to bail out in case of emergency. This is my modification.

function doTheMagic(counter) {    alert("It worked on " + counter);  }    // wait for document ready then call handler function  var checkLoad = function(counter) {    counter++;    if (document.readyState != "complete" && counter<1000) {      var fn = function() { checkLoad(counter); };      setTimeout(fn,10);    } else doTheMagic(counter);  };  checkLoad(0);  


Solution:29

Edit of the edit of @duskwuff to support Internet Explorer 8 too. The difference is a new call to the function test of the regex and the setTimeout with an anonymous function.

Also, I set the timeout to 99.

function ready(f){/in/.test(document.readyState)?setTimeout(function(){ready(f);},99):f();}  


Solution:30

In short, instead of the $(document).ready() used in jQuery, we can use a JavaScript method:

<script>      document.addEventListener("DOMContentLoaded", function_name, false);      function function_name(){          statements;      }  </script>  

Thus, when the page is ready i.e. DOMContentLoaded only then the function function_name() will be invoked.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »