19

Running my (rather complex) JavaScript/jQuery application in Google's Chrome browser, it would appear that $(document).ready fires while some of the JavaScript files have not yet loaded.

The relevant code (simplified):

In my HTML file

<script>var httpRoot='../../../';var verifyLoad = {};</script>

<script src="../../../globalvars.js"></script>
<script src="../../../storagekeys.js"></script>
<script src="../../../geometry.js"></script>
<script src="../../../md5.js"></script>
<script src="../../../serialize.js"></script>
...
<script src="../../../main.js"></script>

As the last statement of each of the .js files except main.js:

verifyLoad.FOO = true; // where FOO is a property specific to the file

e.g.

verifyLoad.jquerySupplements = true; 

verifyLoad.serialize = true; 

In main.js:

$(document).ready(function() {
    function verifyLoadTest (scriptFileName, token) {
        if (!verifyLoad.hasOwnProperty(token)) {
            console.log(scriptFileName + ' not properly loaded'); 
        };
    };
    verifyLoadTest('globalvars.js', 'globalvars');
    verifyLoadTest('storagekeys.js', 'storagekeys');
    verifyLoadTest('geometry.js', 'geometry');
    verifyLoadTest('md5.js', 'geometry');
    verifyLoadTest('serialize.js', 'serialize');
    ...
}

Much to my amazement, I see some of these trigger. This does not match my understanding of $(document).ready. What am I missing?

Joe Mabel
  • 1,372
  • 10
  • 29
  • 2
    Are your scripts in `` or in `` ? – Mikhail Jul 05 '11 at 19:40
  • Assuming the answer to @Mikhail's q is "body". `document.ready` absolutely guarantees that scripts in `head` are available. Scripts in the body *may or may not be* and is not predictable. – Jamie Treworgy Jul 05 '11 at 20:02
  • @jamietre: I'm thinking you're right about OP's scripts being in the *body*. Too bad he won't answer. I wonder if it would make a difference to place a `
    ` or something after the `
    – user113716 Jul 05 '11 at 20:04
  • Script includes are loaded asynchronously, so inline script could be executed before (or after) any given include is loaded. I am pretty sure this applies to inline includes as well. I think op just needs to move the includes to head and all will be well. – Jamie Treworgy Jul 05 '11 at 20:10
  • @jamietre: So you're saying scripts loaded via the ` – user113716 Jul 05 '11 at 20:19
  • Sorry, was having lunch and didn't notice the question. They are in the body. I'll try putting them in the head. – Joe Mabel Jul 05 '11 at 20:20
  • @Patrick, I'm pretty sure Jamieter is right about that. That's certainly always been my understanding. – Joe Mabel Jul 05 '11 at 20:25
  • Yes - they are - though most browsers limit to 2 (I think) scripts loading simultaneously. They are executed in the order they appear. They are loaded independently from the DOM rendering, so inline scripts can easily run before the first include script has finished executing. – Jamie Treworgy Jul 05 '11 at 20:28
  • @jamietre: I just did a test where I placed an 800KB script on a page that was nothing but comments followed at the end by: `var tester = document.getElementById('tester'); console.log( tester );`, and referenced it in an HTML file that was nothing but: `
    hi
    ` *along with typical page structure of course*. The console logged `null` for the result of the `getElementById`. If it loaded asynchronously, there should have been plenty of time for the `tester` element to load.
    – user113716 Jul 05 '11 at 20:46
  • @patrick dw- in your test case is that script in the head or body? Is it a local script file, or from a remote domain? – Jamie Treworgy Jul 05 '11 at 21:49
  • @jamietre: Good questions. The `script` is in the `body`, just before the `div` element (and that encompasses the entire content of the body). The `.html` and `.js` files were both on the same remote server. I ran the test only once, and never ran it locally, to ensure that the script wasn't cached. I'll put the test back together, and provide a temporary link. – user113716 Jul 05 '11 at 21:56
  • I think that remote scripts are subject to different rules for the number of simultaneous events and blocking. Try the test with the include in `` as well (instead of inline). Finally some of this may be browser dependent. I just did a quick profile for a web site I'm working on that has about a dozen js includes, and chrome requests all of them at the same time (they are all in head). The first one begins executing before some others are finished loading. The "DomContentLoaded" event fires before at least one script has finished loading. – Jamie Treworgy Jul 05 '11 at 22:03
  • @jamietre: When you're talking about multiple scripts, I don't doubt that there are optimizations that allow them to begin downloading, but they'll be invoked in sequence. As you noted, the first one was invoked first. I'm quite certain that all the scripts will be invoked in order, and any part of the DOM after a given script will not be parsed until the script (and other DOM elements) that come before have finished. – user113716 Jul 05 '11 at 22:23
  • ...with specific respect to the scripts, I'd say that I'm positive that whether in the head or body, they'll be invoked sequentially. If not, you'd never be able to ensure that dependencies are met. EDIT: I did it with the script in the `head`. Same result. – user113716 Jul 05 '11 at 22:24
  • Yeah you're right - I am getting confused about loading versus executing. The question remains, under exactly what circumstances will document.ready be fired before a script that is loaded statically has finished executing? – Jamie Treworgy Jul 05 '11 at 22:32
  • ...I did another experiment, where in the head I placed a very short script after the very long one. Sure enough, the short one will not execute until after the first one even though according to the timeline it was finished downloading far before the other. – user113716 Jul 05 '11 at 22:33
  • @jamietre: Oops, didn't see that comment that you slipped in there between mine. Yes, that is the question. I have a feeling that when the scripts are at the end and have no more subsequent elements to block except for the closing of the body, the body is perhaps allowed to close. I'll probably do more experimenting with that later. If I see anything interesting, I'll post it here. :o) – user113716 Jul 05 '11 at 22:54

1 Answers1

18

The document's ready event is fired when the browser has parsed the HTML file from beginning to end and converted it into a DOM structure. It does not in any way guarantee that any other resources (e.g. stylesheets, images or, as in this case, scripts) will have loaded. It only refers to the DOM structure, and is fired irrespective of the loading status of the page's resources.

If you want to wait for resources to load, use the window's load event, which is fired only when every element on the page has finished loading.

See:

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • 1
    Still, scripts are blocking when loaded synchronously, so it would seem that `ready()` shouldn't fire until they're fully loaded. – user113716 Jul 05 '11 at 19:50
  • Hmm. I'd rather not wait for all images to load, as well. But I'm already loading most of my images dynamically after `$(document).ready`, so this might be OK. I'll certainly try it. – Joe Mabel Jul 05 '11 at 19:55
  • @Joe: Are you loading the scripts asynchronously? Where are the scripts located in the document (as you were asked above)? – user113716 Jul 05 '11 at 19:56
  • Asynchronously, and I'd hope to keep it that way. There are about 20 of them, and if one is slow I'd hope the others could load in the meanwhile; as far as I can tell, doing it synchronously means doing it serially, and giving up a lot of parallelism. – Joe Mabel Jul 05 '11 at 20:22
  • At least in my test case, this looks like it solves it. (Now I just have to keep my eyes open for whether it messes me up in some other case!) Thanks, interesting that none of the discussions of jQuery coding style I'd read ever suggested this alternative. – Joe Mabel Jul 05 '11 at 20:41
  • 1
    Sorry lonesomeday, maybe I'm wrong, but I really just don't think this is correct. If this was the case, you'd never be able to reliably run any code loaded by a different ` – user113716 Jul 05 '11 at 20:55
  • As it turns out, I have one miserable, unusual case where I download a bunch of images, and I'll probably have to work out how to identify that when it's happening and special-case it. And @patrick, I'm basically with you on the theory of this, but making this change does seem to make my problem go away. It would probably be worthwhile keeping this conversation open, because in terms of general knowledge (as against what works for my app) I suspect we haven't yet hit the really solid ground. – Joe Mabel Jul 05 '11 at 21:14
  • @Joe: Yeah, using `window.onload` is often not desirable. I can't tell from your question exactly where the scripts are loaded. I know they're in the body, but *where* in the body? At the very end? Have you tried placing them in the ``? Or if you want them at the end of the body, try placing a simple `
    ` element *after* the last script. I'd think it might ensure that `ready()` won't run prematurely.
    – user113716 Jul 05 '11 at 21:22
  • They are currently the last thing in the body. If this simple change (`window.onload`) doesn't work out, I'll certainly try these other experiments. – Joe Mabel Jul 05 '11 at 21:30