8

I was wondering if the window.onload = function(){} (or any other kind of onload, like the jQuery $(document).ready(); is necessary if the code is placed at the bottom of my <body>?

Or there could be highly unexpected side-effects?

ajax333221
  • 10,585
  • 14
  • 56
  • 89

3 Answers3

9

Yes, there could be unexpected consequences. But, no, it's not absolutely necessary. The timing could be off for things still loading, like complicated layouts, deep DOM structures, dynamic HTML from other scripts, or images. To avoid these situations, it's always safest to wrap your script in an onload event.

Here are some examples that demonstrate this. All examples tested on Chrome 17.0.963.12 dev on OS X. Browser results may vary when not using onload, which demonstrates its unpredictable behavior. The examples return fail if the result is different than what you'd expect (i.e. what your design specifies) and return success when the result matches what you would expect. With onload they always return success.

Example 1

In this example, the code is expecting the image to be a certain width. If the code is wrapped in an onload event the width is correct, otherwise, it's not.

Demo: http://jsfiddle.net/ThinkingStiff/qUWxX/

HTML:

<div id="result"></div>
<img id='image' src="http://thinkingstiff.com/images/matt.jpg" />

Script:

document.getElementById( 'result' ).innerHTML 
    = document.getElementById( 'image' ).offsetWidth == 346 ? 'success': 'fail';

You'll see the jsFiddle is set to "onLoad" in the upper left corner of the page and the result above the image is success.

enter image description here

Change that to "onDomReady" or "no wrap (body)":

enter image description hereenter image description here

Now press "Run" at the top left of the page:

enter image description here

The result above the image will now be fail.

Example 2

Here is another example that doesn't use images. In this one, an inline script has been added to the HTML. The code is expecting the width to be what it was set to by the inline script. With onload it's corrent, without, it's not. Use the same instructions as before for this demo.

Demo: http://jsfiddle.net/ThinkingStiff/n7GWt/

HTML:

<div id="result"></div>
<div id="style"></div>

<script>
    window.setTimeout( function() { 
        document.getElementById( 'style' ).style.width = '100px'; 
    }, 1 );
</script>

Script:

document.getElementById( 'result' ).innerHTML 
    = document.getElementById( 'style' ).style.width ? 'success' : 'fail';

Example 3

Here's an example that uses no images or Javascript in the body, just CSS. Again, the results are different between onload and not.

Demo: http://jsfiddle.net/ThinkingStiff/HN2bH/

CSS:

#style {
    animation:             style 5s infinite;
        -moz-animation:    style 5s infinite;
        -ms-animation:     style 5s infinite;
        -o-animation:      style 5s infinite;
        -webkit-animation: style 5s infinite;
    border: 1px solid black;
    height: 20px;
    width: 100px;    
}

@keyframes             style { 0% { width: 100px; } 100% { width: 500px; } }
    @-moz-keyframes    style { 0% { width: 100px; } 100% { width: 500px; } }
    @-ms-keyframes     style { 0% { width: 100px; } 100% { width: 500px; } }
    @-o-keyframes      style { 0% { width: 100px; } 100% { width: 500px; } }
    @-webkit-keyframes style { 0% { width: 100px; } 100% { width: 500px; } }

HTML:

<div id="result"></div>
<div id="style"></div>

Script:

document.getElementById( 'result' ).innerHTML 
    = document.getElementById( 'style' ).clientWidth > 100 ? 'success' : 'fail';

There are just too many scenarios where not wrapping your code can cause issues that you won't be able to anticipate. To avoid these situations, it's always safest to wrap your script in an onload event.

ThinkingStiff
  • 62,391
  • 29
  • 139
  • 237
  • 1
    I don't believe this is true. I have never personally witnessed a timing error when I didn't wrap my code in a `DOMContentLoaded` handler. Of course, you will need a `load` handler if non-DOM resources need to be loaded first. – Jeffrey Sweeney Jan 03 '12 at 19:28
  • I disagree with this too. If the HTML that is going to be referenced by the script is above the script, I've never had a problem. If you need to access images from your script, they might not be loaded – Juan Mendes Jan 03 '12 at 19:32
  • @JeffreySweeney So you think there could be **no** unexpected consequences, ever? What about when the the OP discovers `async` or `defer` as a way to speed things up? Or they start adding different script files for jQuery plugs-ins that create dynamic HTML? Is the statement, "It's always safest..." not true? When is it unsafe to use `.onload`? – ThinkingStiff Jan 03 '12 at 19:39
  • @JuanMendes I still argue it's always safest to use it. See my comment to Jeffrey. – ThinkingStiff Jan 03 '12 at 19:39
  • @ThinkingStiff Loading external scripts = loading external resources (forgive me if I misread your latter point), but I'll agree with you on your former point. I wasn't inferring that it was unsafe to wrap code in a `DOMContentLoaded` (or `onload` for external resources), only that it was unnecessary. – Jeffrey Sweeney Jan 03 '12 at 19:44
  • @ThinkingStiff: I would love to see an example where the scripts at bottom fails; I don't see how the two examples you mentioned would cause problems. Your statement *is always safest* is too broad and could degrade performance (waiting too long), that is why I found it important to comment on this answer. – Juan Mendes Jan 03 '12 at 21:36
  • @JuanMendes I updated the answer with an example. The OP just asked if there could be unexpected consequences, and there can be. I removed the `DOMContentLoaded` recommendation and further clarified the "always use". – ThinkingStiff Jan 03 '12 at 22:29
  • @JeffreySweeney I added an example, removed `DOMContentLoaded`, and further qualified `onload`. – ThinkingStiff Jan 03 '12 at 22:34
  • @ThinkingStiff: Your example uses images. My comment already mentioned that if you need images to be loaded in your script, you must use the onload event. Therefore, I still think it's unnecessary if your script doesn't assume images are preloaded. – Juan Mendes Jan 04 '12 at 00:48
  • 3
    @JuanMendes I added another example that doesn't use images. – ThinkingStiff Jan 04 '12 at 01:32
  • @ThinkingStiff: Thanks for all your work here. I'm always glad to be proven wrong. However, I don't understand what your jsfiddle is supposed to test. If the width of the div is not greater than 100, than it's a fail? Why? That just proves that it was called after the animation has started. In any case, my browser (Chrome 16, Win 7) *failed* for "onLoad", "nowrap" and "onDomReady". I'd hate making your whole code run slower because you don't understand the difference between DOMReady (or scripts at bottom), and the onload event – Juan Mendes Jan 04 '12 at 18:25
  • @JuanMendes The `fail` just means `onload` and `no wrap` act differently. Since the results are different (at least on the browser I tested), your code could be expecting one result and instead gives you the other. With `onload` you always get the same expected result: widths, heights, locations are all exactly what you set them to in your designs. – ThinkingStiff Jan 04 '12 at 18:41
  • @ThinkingStiff: But with onload, you must wait until all your external files are loaded. That is a cost I'm not willing to accept. Yes, the two "events" behave differently and you need to know the difference. If your script depends on images, you must wait until onload, otherwise, bottom of body tag is fine – Juan Mendes Jan 04 '12 at 19:41
  • If "the code is expecting the image to be a certain width" then the code should implement an event listener to provide this certain image is loaded ..but not that all images and css are loaded. onLoad is fired lateley. Code should not depend on document.onLoad. Browsers build Dom tree immediately as they parse the document so placing script just before the closing `body` tag should do the job. Your second example also doesnt look as real life scenario (I get it but ... setTimeout? if interval was a little bit bigger onLoad doesnt help too).
    I dont get your point at all.
    – Boris D. Teoharov May 26 '13 at 16:50
2

Couple of different things going on.

  1. onload is called only after embedded content such as images is loaded. This means you can put code in onload that depends on that content being there.
  2. ready handlers are fired before that, when the DOM (ie internal structure) of your page is fully loaded. This isn't that different from putting it at the bottom, but one difference is that if someone navigates away from your page and then back, these handlers will fire again.

Technically scripts that run at the end of the document can use methods like getElementById to pull in elements that are already rendered. You may still want to put those in a ready or load handler for the above reasons. This isn't to say the scripts themselves shouldn't be at the bottom - there's still a benefit to perceived performance from having them there.

Dan
  • 10,380
  • 4
  • 46
  • 74
  • Would you have some good articles explaining why code at the bottom of the page is inadvisable? I'm interested to know more about this. – Jeffrey Sweeney Jan 03 '12 at 19:25
  • I disagree that it's not advisable, http://developer.yahoo.com/performance/rules.html still suggests scripts at the bottom – Juan Mendes Jan 03 '12 at 19:33
  • Sorry, I wasn't suggesting that scripts shouldn't go at the bottom. I'll revise that. – Dan Jan 03 '12 at 19:35
1

A script tag at the bottom of an HTML page is equivalent to DOMContentLoaded. All the html code has been downloaded, and Javascript is now capable of accessing DOM elements.

load is called when all other resources, such as images, have completely downloaded.

Jeffrey Sweeney
  • 5,482
  • 4
  • 19
  • 29