Reintroducing the <script> Tag

At the risk of sounding hyperbolic, <script> is the most important HTML tag of all time. Don’t believe me? Consider that a page consisting entirely of a single script tag can do anything. It can spin itself a document from whole cloth, loading whatever resources it wants. By contrast, even the most marvelous page without a <script> tag is sharply limited, unable to respond to the user’s actions with anything more complicated than a CSS transition.

In modern browsers, <script> comes in two refreshing flavors: classic and nonblocking. In this section, we’ll see how you can use both varieties to make your pages load as quickly as possible.

Where the Blocking Scripts Go

A standard-issue <script> tag is commonly said to be blocking. That term has to be understood in context: when a modern browser sees a blocking <script> tag, it will continue to read the document past that point and download other resources (scripts and style sheets). However, it won’t evaluate those resources until the script has been fully downloaded and run.

So, if you have five blocking <script> tags in the <head> of the document, the user won’t see anything but the page’s title until all five scripts have been downloaded and run. Further, while those scripts are being run, they’ll be able to see the document only up until that point. If they want to see any of the good stuff that’s waiting down in the <body>, they’ll have to bind a handler to an event like document.onreadystatechange.

For that reason, it’s become fashionable to put scripts at the end of the page’s <body>. This way, the user gets to see the page more quickly, and the scripts get to see the DOM in all its glory without having to wait for an event to fire. For most scripts, this move is a big improvement.

But not all scripts are alike. Before you move a script down, ask yourself three questions.

  • Is there a chance that this script will be called directly from inlined JavaScript in the <body>? This may be obvious, but it’s worth double-checking.

  • Does this script allow older browsers to recognize HTML5 elements? Modernizr[55] does, which is why the HTML5 Boilerplate[56]—an exemplar of best practices—includes it right up top.

  • Is this script something that will determine how your page looks when it’s rendered? An example is Typekit’s hosted fonts. If you put Typekit’s script at the end of your document, the text on your page will render twice: once immediately and again after the script runs.

If the answer is “yes” to any of these questions, that script should go in the <head>. Otherwise, it should go in the <body>. Minifying and concatenating those two sets of scripts will give you a document that looks like this:

 
<html>
 
<head>
 
<!-- metadata and stylesheets go here -->
 
<script​ src=​"headScripts.js"​​>​​</scripts>
 
</head>
 
<body>
 
<!-- content goes here -->
 
<script​ src=​"bodyScripts.js"​​>​​</script>
 
</body>
 
</html>

This is great for load times, but be aware that it may give the user a chance to interact with the page before bodyScripts.js is loaded.

Deferring Scripts

In the last section, I recommended <body> placement for most scripts, since that allows the user to see the page more quickly and avoids the overhead of binding to a “ready” event before manipulating the DOM. But there is a downside: the browser won’t be able to start loading those scripts until the whole document is loaded. For large documents being sent over slow connections, this can be a major bottleneck.

Ideally, we’d load those scripts in parallel with the document, without delaying DOM rendering. Then when the document is ready, we’d run the scripts because they’re loaded while preserving the order of the <script> tags.

If you’ve read the book up to this point, no doubt you’re excited about writing a custom Ajax script loader to meet these requirements! But most browsers support a simpler solution.

 
<script​ defer src=​"deferredScript.js"​​>

Adding that defer attribute tells the browser this: “Start loading this script right away, but don’t run it until the document is ready and all previous scripts with defer have finished running.” Placing a deferred script in the <head> of your document gives you all the advantages of <body> placement, plus a substantial speed boost in large documents!

The downside? defer isn’t supported by all browsers. Notably, as of this writing, even the latest Opera ignores the attribute.[57] That means if you want to ensure that your deferred scripts run after the document is loaded, you’ll have to wrap each script’s code in something like jQuery’s $(document).ready. That may be worthwhile, since something like 97 percent of your visitors will get to enjoy the benefits of parallel loading, while the other 3 percent will still get perfectly functional JavaScript.

With defer, we can improve on the page example from the previous section by replacing bodyScripts.js with deferredScripts.js.

 
<html>
 
<head>
 
<!-- metadata and stylesheets go here -->
 
<script​ src=​"headScripts.js"​​>​​</scripts>
 
<script​ defer src=​"deferredScripts.js"​​>​​</script>
 
</head>
 
<body>
 
<!-- content goes here -->
 
</body>
 
</html>

Just remember that it’s important to wrap deferredScripts so that it won’t run until after the document ready event in browsers that don’t support defer. If the body content is more than a couple of kilobytes, that trade-off is worthwhile.

Full Script Parallelization

If you’re a hardcore every-millisecond-counts page load gearhead, defer may sound like weak sauce to you. You don’t want to wait until all previous scripts with defer have run. And you certainly don’t want those scripts to wait to run until the document is ready, not when they have to use $(document).ready anyway for Opera’s sake. You just want to load those scripts as soon as possible and run them as soon as possible.

That’s why modern browsers offer the async attribute.

 
<script​ async src=​"speedyGonzales.js"​​>
 
<script​ async src=​"roadRunner.js"​​>

If defer makes you think of an orderly queue waiting for the document to load, async should make you think of anarchy. Those two scripts shown earlier could run in any order, and they’ll run as soon as the JavaScript engine is available to run them, whether the document is ready or not. So, aside from feeling the need for speed, why would you use async?

For most scripts, async is a tough sell. It’s not as widely supported as defer, so fewer users will notice the performance boost.[58] And because async scripts can run at any time, it’s all too easy to introduce Heisenbugs that depend on when a script happens to finish loading.

But for scripts that are intended to be independent, async is a small but significant win. Got a third-party script that adds a feedback widget and another that adds a tech support chat box? The page will run fine without them, and it doesn’t matter which one runs first. So, you can get a free speed boost by using async with them.

Adding a couple of independent widgets to our last page example, we get the following:

 
<html>
 
<head>
 
<!-- metadata and stylesheets go here -->
 
<script​ src=​"headScripts.js"​​>​​</scripts>
 
<script​ src=​"deferredScripts.js"​ defer​>​​</script>
 
</head>
 
<body>
 
<!-- content goes here -->
 
<script​ async defer src=​"feedbackWidget.js"​​>​​</script>
 
<script​ async defer src=​"chatWidget.js"​​>​​</script>
 
</body>
 
</html>

This page structure makes your priorities clear. In the vast majority of browsers, DOM rendering will be delayed only until headScripts.js finishes running. deferredScripts.js will load in the background while the DOM is rendered. Then, after DOM rendering, deferredScripts.js and the two widget scripts will run. The order in which those scripts run is indeterminate in browsers that support async. If you’re not sure whether that’s OK, don’t use async!

In this section, we’ve seen how a <script> tag’s placement and attributes you use can make a big difference in the time it takes for someone to start using your page. In the next section, we’ll see how you can take the principle of async loading even further by using scripts to load other scripts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.56.233