Friday, June 11, 2010

Avoid the "inline javascript sandwich"

Hopefully by now it's clear that javascript in the head is bad - it blocks parallel requests from loading (for all but the newest browsers).  If you can't move it all to the bottom of your document or make it async then we usually recommend combining it into a single file and putting it after the CSS so that it will load in parallel with the CSS and reduce the pain.  This works because it's actually the EXECUTION of the javascript that causes the browser to block any other requests but it will go ahead and fetch the javascript file in parallel to whatever it is currently loading (css) to get it ready to execute.

I've bumped into an issue with this a few times recently on pages I've been looking at so I figured it was worth warning everyone to avoid this:

<link rel="stylesheet" type="text/css" href="my.css" />
<script type="text/javascript">
    var1='some value';
    var2=true;
</script>
<script type="text/javascript" src="my.js"></script>
That little bit of inline javascript causes the browser to not load the external js at the same time as the css because it needs to block to execute the code.


Thanks to Steve's handy Cuzillion app I threw together a quick page with exactly that structure and this is how it loads:




Move the inline javascript up above the css


As long as the javascript isn't going to be modifying the css, you'll be a lot better off moving the inline code up above the css so it can execute without having to block anything else.  If you're just setting variables for later javascript to use then this is a no-brainer.

Here is what it looks like with the inline javascript moved up above the css:




The javascript is back where it should be, loading in parallel to the css.

10 comments:

  1. Patrick,

    What happens if you place a DEFER attribute on the inline SCRIPT block?

    ReplyDelete
  2. Everything I've read indicates that if you care about the execution order of the code you probably don't want to do that (if you're defining variables in the inline code, deferring it could cause the code that needs the variables defined to execute before they are defined).

    It also looks like defer isn't (or wasn't) really supported outside of IE so it wouldn't get you the same bang as just moving the code block.

    ReplyDelete
  3. Defer is supported by Firefox 3.5+ (with changes in 3.6 to ban the attribute on inline scripts, prompted by changes in the HTML5 spec). Support in Webkit is a work in progress.

    ReplyDelete
  4. For the most part the newer browsers do not have the same blocking problem with javascript that the earlier browsers do so you won't see the problem above on IE8, Firefox 3.6+ or Chrome (not sure which version added non-blocking).

    That said, even if the new browsers had the problem, defer wouldn't help because it's the inline script you'd need to defer.

    ReplyDelete
  5. I found a way to have inline JS + <script> includes at the bottom :
    - put JS at the bottom and mute the errors
    - execute the page
    - unmute errors
    - include the <script>
    - eval() all inline scripts

    full post here, in french (but code examples and graphics are readable) :
    http://jpv.typepad.com/blog/2010/05/performances-web-put-scripts-at-the-bottom-oui-mais-comment-.html

    ReplyDelete
  6. @jpvincent: what does mute errors mean?

    ReplyDelete
  7. What happens if you place a DEFER attribute on the inline SCRIPT block?

    see here
    What happens if you place a DEFER attribute on the inline SCRIPT block?

    ReplyDelete
  8. mute means catching the onerror JS and setting a dummy function in order for the user not to see the errors throwned by the JS engine

    ReplyDelete
  9. My site shows the Inline script block #2 Dreamstopixels I checked everywhere inside header.php and index.php for inline scripts but i couldn't find what was the error, if anyone knows how to fix it please send an email to me Thanks

    ReplyDelete
  10. FYI, this "sandwich problem" seems to be no longer relevant with modern browsers for the past few years. I bet that preloaders are smarter now and will more agressively fetch external scripts after inline script blocks (even if the inline script could theoretically render that download useless).

    I could replicate the issue in Firefox 10, but the problem didn't exist in the oldest versions of IE and Chrome that I could test (IE 9 and Chrome 14).

    ReplyDelete

All comments are moderated and may take a while to appear.

Note: Only a member of this blog may post a comment.