javascript:how to write $(document).ready like event without jquery
Asked Answered
R

7

33

in jquery $(document).ready(function) or $(function) , how could I do the same thing without jquery, and I need browser compatiable, and allow to attach more than one function.

Note: dom ready!= window onload

Runnels answered 21/10, 2010 at 15:26 Comment(5)
I looked at the jQuery source code and there is quite a chunk of code related to the ready() handler. If I read the code correctly, the event is called DOMContentLoaded. IE does not support this, so onreadystatechange is used instead.Quickel
Also, it has to do with document.readyState having the value "complete"Quickel
@Šime Vidas: It has to do with many things (iframe loading has its own quirks? Who knew?), and quite frankly, it's an amazing piece of code. I was reminded why I no longer register events directly - it's a major PITA to do it right.Farlay
possible duplicate of $(document).ready equivalent without jQueryEyecup
I believe you are better off using jQuery than reinventing the wheel. It just has too many quirks (read browser compatibility issues) to think ofLutes
F
24

This is the way jQuery wraps the functions you're looking for - the snippet does not need jQuery, and is cross-browser compatible. I've replaced all calls to jQuery.ready() with yourcallback - which you need to define.

What goes on in here:

  • first, the function DOMContentLoaded is defined, which will be used when the DOMContentLoaded event fires - it ensures that the callback is only called once.
  • a check if the document is already loaded - if yes, fire the callback right away
  • otherwise sniff for features (document.addEventListener / document.attachEvent) and bind the callbacks to it (different for IE and normal browsers, plus the onload callback)

Lifted from jQuery 1.4.3, functions bindReady() and DOMContentLoaded:

/*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
// Cleanup functions for the document ready method
// attached in the bindReady handler
if ( document.addEventListener ) {
DOMContentLoaded = function() {
    document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
    //jQuery.ready();
            yourcallback();
};

} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
    // Make sure body exists, at least, in case IE gets a little overzealous 
            if ( document.readyState === "complete" ) {
        document.detachEvent( "onreadystatechange", DOMContentLoaded );
        //jQuery.ready();
                    yourcallback();
    }
    };
}

// 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( jQuery.ready, 1 );
    // ^^ you may want to call *your* function here, similarly for the other calls to jQuery.ready
    setTimeout( yourcallback, 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", jQuery.ready, false );
    window.addEventListener( "load", yourcallback, 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", yourcallback );

 }

That's 51 lines of pure JavaScript code, just to register the event reliably. As far as I know, there is no easier method. Goes to show what the wrappers like jQuery are good for: they wrap the capability sniffing and ugly compatibility issues so that you can focus on something else.

Farlay answered 21/10, 2010 at 15:45 Comment(7)
I gotta say it's a lot of complicated code for knowing when to initialize your scripts that use the DOM... Like I posted in my answer, I just call a single initializer function from after the close body tagDentoid
@Juan Mendes: Yes, that does something superficially similar - but inline script tags have their quirks, too: blocking parallel downloads in cough cough certain browsers, for example. And don't forget the whole "let's modify the DOM while it's still loading" barrel of fun. That's why I'd rather go with $.ready() - one well-cached library on Google's CDN and one well-working function call later, It Just Works. But if you want to reinvent all the JS hacks since 1997, go right ahead.Farlay
@Piskvor: "But if you want to reinvent all the JS hacks since 1997, go right ahead"? How is this a hack? I think it's a simple (elegant) solution. You're guaranteed that the DOM is ready if you place a script tag after the body tag, it's not "let's modify the DOM while it's still loading"Dentoid
@Juan Mendes: I've misread your comment - are you saying "let's deliberately break web standards?" </body>anything that's not whitespace</html> is tag soup, but not HTML (and absolutely not XHTML). That, in turn, forces the quirks mode rendering (i.e. "the page is completely broken, let's try to read the author's mind" mode). If you think - in 2010 - that QM is a good thing (elegant even!), it is pointless to try to convince you otherwise. Have fun with your pages that render differently in each browser. As for me, I've had enough of this compatibility hell in the past decade.Farlay
@Piskvor. I didn't know that adding a script tag after the body tag triggered QM. Can you point me to a resource with further information? By the way your 'smart' comments are assuming things that I didn't say, I never said that triggering QM was elegant and I never said that you should modify the DOM while it's loading, please be more courteous with replies.Dentoid
@Juan Mendes: I apologize, I got a bit carried away there.Farlay
Can you point me to something that talks about quirks mode and scripts at the bottom of the page? developer.yahoo.com/performance/rules.html is the link that suggests you should always leave your scripts at the bottom of the page.Dentoid
T
9

Smallest DOMReady code, ever.

<html>
  <head>
    <script>
      var ready = function (f) {
        (/complete|loaded|interactive/.test(document.readyState)) ?
            f() :
            setTimeout(ready, 9, f);
      };
    </script>
  </head>
  <body>
    <script>
      ready(function () {
        alert('DOM Ready!');
      });
    </script>
  </body>
</html>
Thanh answered 11/8, 2011 at 13:41 Comment(4)
Quick question: what is the "r" function? I get an error when trying this in FF.Bahia
note that this uses eval() under the hood. But cool trick man!Cohen
use "setTimeout(ready, 9, f)" if you are not in ie or "setTimeout(function () { ready(f); }, 9)" if you areThanh
Your condition is reversed.Patrilineal
M
3

This is all you need if you're supporting IE9+ and modern (2013) versions of Chrome, FF, Safari, etc.

function ready(event) {
    // your code here
    console.log('The DOM is ready.', event);
    // clean up event binding
    window.removeEventListener('DOMContentLoaded', ready);
}

// bind to the load event
window.addEventListener('DOMContentLoaded', ready);
Manuel answered 30/4, 2013 at 6:35 Comment(0)
A
2

Here's a method I use that seems to work reliably

function ready(func) {
  var span = document.createElement('span');
  var ms = 0;
  setTimeout(function() {
    try {
      document.body.appendChild(span);

      document.body.removeChild(span);

      //Still here? Then document is ready
      func();
    } catch(e) {
      //Whoops, document is not ready yet, try again...

      setTimeout(arguments.callee, ms);
    }
  }, ms);
}

Pretty simple, it just keeps trying to append an empty <span> element to document.body. If the document is not "ready" an exception will be thrown, in which case it tries again with a new setTimeout call. Once no exception is thrown, it calls the callback function.

I'd be happy to hear if there are any problems with this method. It has worked well for me, but I have not done the extensive testing that would be natural to any popular Javascript framework.

Aviate answered 21/10, 2010 at 18:15 Comment(2)
Two words: memory leaks. On older browsers, with all the element adding and removing, you'll be leaking memory like crazy. Plus, you're reimplementing the busy-wait loop in JavaScript (which is not the most efficient language as it is).Farlay
I'm not seeing it. With this method, an element is only added and removed once, when the document is ready. And even then only one element is created in memory outside the timeout function. Also, setTimeout/setInterval with 0 ms delay is not the same as a conditional while loop. With the way timers work in JS, and the simplicity of the operations within the timeout function of this example, performance should not be significantly affected.Torques
D
1

I've seen lots of different ways of trying to do this. The simplest way (suggested by yahoo initially, I think) is to just call your initializer function after the close body tag, a bit obtrusive, but it's a single line.

Dentoid answered 21/10, 2010 at 17:45 Comment(0)
A
0

Edit

The DomReady event does not exist nativly in javascript. You can implement your own by following some wonderful work done by people like Dean Edwards here and here with those in place you can perform a similar event attachment process on document instead of window.


Check out user83421's answer to How do I add an additional window.onload event in Javascript

To recap here as well.

if (window.addEventListener) // W3C standard
{
  window.addEventListener('load', myFunction, false); // NB **not** 'onload'
} 
else if (window.attachEvent) // Microsoft
{
  window.attachEvent('onload', myFunction);
}
Allonge answered 21/10, 2010 at 15:30 Comment(1)
The snippet you have is just for the load eventDentoid
E
0

I have this after the 'close body' tag and before the 'close html' tag. and it works pretty well. The load presets function assigns width,height and position values to css div tags. useful for different screen sizes.

document.onreadystatechange = function () {
if  (document.readyState == "interactive") {
loadPresets();
}

}

Earsplitting answered 24/7, 2014 at 2:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.