Problem: JavaScript loading blocks page rendering

JavaScript files are static files, just as images and CSS files. However, unlike images, when a JavaScript file is loaded or executed using a <script> tag, rendering of the page is suspended.

This makes sense, because the page may contain script blocks after the <script> tag that are dependent on the JavaScript file. If loading of a JavaScript file didn't block page rendering, the other blocks could be executed before the file had loaded, leading to JavaScript errors.

Confirming with a test site

You can confirm that loading a JavaScript file blocks rendering of the page by running the website in the folder JavaScriptBlocksRendering in the downloaded code bundle. This site consists of a single page that loads a single script, script1.js. It also has a single image, chemistry.png, and a stylesheet style1.css. It uses an HTTP module that suspends the working thread for five seconds when a JavaScript file is loaded. Images and CSS files are delayed by about two seconds. When you load the page, you'll see that the page content appears after only about five seconds. Then after two seconds, the image appears, unless you use Firefox, which often loads images in parallel with the JavaScript.

If you make a Waterfall chart, you can see how the image and stylesheet are loaded after the JavaScript file, instead of in parallel:

To get the delays, run the test site on IIS 7 in integrated pipeline mode. Do not use the Cassini web server built into Visual Studio.

If you find that there is no delay, clear the browser cache. If that doesn't work either, the files may be in kernel cache on the server—remove them by restarting IIS using Internet Information Services (IIS) Manager. To open IIS manager, click on Start | Control Panel, type "admin" in the search box, click on Administrative Tools, and then double-click on Internet Information Services (IIS) Manager.

Integrated/Classic Pipeline Mode As in IIS 6, every website runs as part of an application pool in IIS 7. Each IIS 7 application pool can be switched between Integrated Pipeline Mode (the default) and Classic Pipeline Mode. In Integrated Pipeline Mode, the ASP.NET runtime is integrated with the core web server, so that the server can be managed for example, via web.config elements. In Classic Pipeline Mode, IIS 7 functions more like IIS 6, where ASP.NET runs within an ISAPI extension.

Approaches to reduce the impact on load times

Although it makes sense to suspend rendering the page while a <script> tag loads or executes JavaScript, it would still be good to minimize the time visitors have to wait for the page to appear, especially if there is a lot of JavaScript to load. Here are a few ways to do that:

Start loading JavaScript after other components have started loading, such as images and CSS files. That way, the other components load in parallel with the JavaScript instead of after the JavaScript, and so are available sooner when page rendering resumes.

Load JavaScript more quickly. Page rendering is still blocked, but for less time.

Load JavaScript on demand. Only load the JavaScript upfront that you need to render the page. Load the JavaScript that handles button clicks, and so on, when you need it.

Use specific techniques to prevent JavaScript loading from blocking rendering. This includes loading the JavaScript after the page has rendered, or in parallel with page rendering.

These approaches can be combined or used on their own for the best tradeoff between development time and performance. Let's go through each approach.

Approach: Start loading after other components

This approach aims to render the page sooner by loading CSS stylesheets and images in parallel with the JavaScript rather than after the JavaScript. That way, when the JavaScript has finished loading, the CSS and images will have finished loading too and will be ready to use; or at least it will take less time for them to finish loading after the JavaScript has loaded.

To load the CSS stylesheets and images in parallel with the JavaScript, you would start loading them before you start loading the JavaScript. In the case of CSS stylesheets that is easy—simply place their <link> tags before the <script> tags:

Starting the loading of images is slightly trickier because images are normally loaded when the page body is evaluated, not as a part of the page head.

In the test page you just saw with the image chemistry.png, you can use a bit of simple JavaScript to get the browser to start loading the image before it starts loading the JavaScript file. This is referred to as "image preloading" (page PreLoadWithJavaScript.aspx in the folder PreLoadImages in the downloaded code bundle):

When the page is rendered after the JavaScript has loaded, the image and CSS files have already been loaded; so the image shows up right away.

A second option is to use invisible image tags at the start of the page body that preload the images. You can make the image tags invisible by using the style display:none. You would have to move the <script> tags from the page head to the page body after the invisible image tags, as shown (page PreLoadWithCss.aspx in folder PreLoadImages in the downloaded code bundle):

Although the examples we've seen so far preload only one image, chemistry.png, you could easily preload multiple images. When you do, it makes sense to preload the most important images first, so that they are most likely to appear right away when the page renders. The browser loads components, such as images, in the order in which they appear in the HTML, so you'd wind up with something similar to the following code:

Approach: Loading JavaScript more quickly

The second approach is to simply spend less time loading the same JavaScript, so that visitors spend less time waiting for the page to render. There are a number of ways to achieve just that:

Techniques used with images, such as caching and parallel download

Free Content Delivery Networks

GZIP compression

Minification

Combining or breaking up JavaScript files

Removing unused code

Techniques used with images

JavaScript files are static files, just like images and CSS files. This means that many techniques that apply to images apply to JavaScript files as well, including the use of cookie-free domains, caching, and boosting parallel loading.

Free Content Delivery Networks

Serving static files from a Content Delivery Network (CDN) can greatly reduce download times, by serving the files from a server that is close to the visitor. A CDN also saves you bandwidth because the files are no longer served from your own server.

A number of companies now serve popular JavaScript libraries from their CDNs for free. Here are their details:

Microsoft Ajax Content Delivery Networkhttp://www.asp.net/ajaxlibrary/cdn.ashx Serves libraries used by the ASP.NET and ASP.NET MVC frameworks including the jQuery library and the jQuery Validation plugin

In ASP.NET 4.0 and later, you can get the ScriptManager control to load the ASP. NET AJAX script files from the Microsoft AJAX CDN instead of your web server, by setting the EnableCdn property to true:

One issue with loading libraries from a CDN is that it creates another point of failure—if the CDN goes down, your site is crippled.

GZIP compression

IIS has the ability to compress content sent to the browser, including JavaScript and CSS files.

Compression can make a dramatic difference to a JavaScript file as it goes over the wire from the server to the browser. Take for example the production version of the jQuery library:

Uncompressed

Compressed

jQuery library

78 KB

26 KB

Compression for static files is enabled by default in IIS 7. This immediately benefits CSS files. It should also immediately benefit JavaScript files, but it doesn't because of a quirk in the default configuration of IIS 7.

Not all static files benefit from compression; for example JPEG, PNG, and GIF files are already inherently compressed because of their format. To cater to this, the IIS 7 configuration file applicationHost.config contains a list of mime types that get compressed when static compression is enabled:

If you look closely, you'll see that the .js extension is mapped by default to a mime type that isn't in the list of mime types to be compressed when static file compression is enabled.

The easiest way to solve this is to modify your site's web.config, so that it maps the extension .js to mime type text/javascript. This matches text/* in the list of mime types to be compressed. So, IIS 7 will now compress JavaScript files with the extension .js (folder Minify in the downloaded code bundle):

Keep in mind that IIS 7 only applies static compression to files that are "frequently" requested. This means that the first time you request a file, it won't be compressed! Refresh the page a couple of times and compression will kick in.

Minifying a JavaScript file

JavaScript files tend to contain comments and white space that make the file more readable for humans, but these are superfluous as far as the browser is concerned. Minifying a JavaScript file gets rid of those comments and white space, thereby reducing its size and download time. It also encourages developers to write more comments, because they know they won't add to the size of the file as it goes over the wire.

Some minification tools also shorten variable names. This is useful if your server does not use GZIP compression. If compression is enabled, shortening variable names will have little effect, because the compression algorithm itself is very good at shortening repeating strings, such as variable names.

Tools

Here are a number of tools that will minify a JavaScript file for you:

YUI Compressorhttp://developer.yahoo.com/yui/compressor/ This is another standalone Java-based JavaScript compressor that renames local references and removes white space and comments. It can also be used to minify CSS stylesheets.

Yahoo! UI Library: YUI Compressor for .Nethttp://yuicompressor.codeplex.com/ It is an excellent .NET port of the YUI Compressor Java project. It includes a DLL that allows you to minify JavaScript in your C# code, as shown:

CompressorRaterhttp://compressorrater.thruhere.net/ It is an online tool that allows you to test various minification programs with your JavaScript code, to see which one is the most effective.

Impact of minification

How much effect does minification have compared with GZIP compression? Here are the results of applying GZIP compression and minification (using YUI Compressor) on the uncompressed debug version of the jQuery library:

Minified

Reduction through minification

No

Yes

GZIP

No

22 KB

78 KB

65 percent

Compressed

Yes

48 KB

26 KB

46 percent

This shows that minification has the most dramatic impact if GZIP compression has been disabled on the server (which is not recommended). Still, even with compression enabled, minification makes sense, as the drop from 48 KB to 26 KB is significant.

Implementing minification

A common way to implement minification is to run a minifier program during a release build that minifies the JavaScript files. However, this means that you can't simply copy updated JavaScript files to the production server without going through the minification step.

An alternative is to place JavaScript files unminified in production, and have the website minify the files on the fly before serving them to the browser. To save CPU cycles, you would cache the minified file. To ensure that the site immediately picks up a new version of the file, the cached file would have a file dependency on the physical (uncompressed) file on disk.

HTTP handler

You can implement this approach with an HTTP Handler. This is simply a class derived from IHttpHandler, which you put in the App_Code folder, or in a separate class library. To do the actual minification, you can use the YUI Compressor for .NET library, which you saw earlier. The result is code as shown (folder Minify in downloaded code bundle):

Add a Cache-Control header to ask the browser to cache the JavaScript file for 31,536,000 seconds (365 days). You may want to reduce this if it is critical that browsers quickly pick up changes in your JavaScript.

Enabling GZIP compression for dynamic files

Now that you're using ASP.NET to produce the JavaScript, you need to enable compression for dynamic files to have your JavaScript compressed by the server.

Combining or breaking up

A popular approach to reducing JavaScript load times is to combine several JavaScript files into a single big file, so only that one big file is loaded. An alternative is to do the reverse and break up a big JavaScript file into several smaller files that are loaded in parallel. Here is when each of these approaches makes sense.

Older browsers such as Internet Explorer 6 load the scripts one after the other. That way, the browser can be sure that if a script executes code that relies on a previous script, there will be no JavaScript errors. For example, loading the test page in IE 6 resulted in this Waterfall chart:

In this scenario, it is attractive to combine files. This is because loading involves a lot more than just loading the bytes in the file. It also involves overhead, such as sending the request from the browser to the server. A site that requests four files one after the other spends four times as much time on the overhead as a site that places a single request for a single large file.

When and why to break up

Loading JavaScript files one after the other may be very safe, but is also wasteful. The browser could load files in parallel, but hold off executing them until the previous files have loaded. Newer browsers, such as Internet Explorer 7 and 8, Firefox, and Google Chrome do exactly the same thing. For example, loading the test page in IE 7 resulted in the following Waterfall chart:

This makes it attractive to consider splitting large JavaScript files into a number of smaller ones, and loading them in parallel. The request overhead would then be incurred in parallel, rather than sequentially. Loading two half-sized files in parallel may take slightly over half the time taken by the single original file, depending on the time taken by the request overhead.

If you load many JavaScript, CSS, and image files in parallel, keep in mind that modern browsers have a download limit of six files per domain.

Measuring each scenario

To investigate this a bit further, I ran tests with two simple test pages—a page separate.html that loads four JavaScript files and a page combined.html that loads a single big JavaScript file, in which the four separate files are combined. A bit of JavaScript in the HTML files measured the total download time. See folder ScriptLoadTest in the downloaded code bundle.

Because the sizes of the JavaScript files might affect the results, I ran a "big" scenario with one 220-KB JavaScript file and three 37-KB files, and a "small" scenario with four 10-KB files. The test pages sat on my hosting account. Your numbers are bound to vary, but you'll get a general idea.

The results for the big scenario were:

Total download times (seconds)

combined.html

separate.html

IE 6

1.3

1.5

IE 7

1.4

0.9

IE 8

1.3

0.7

Firefox 3

1.4

1.2

Google Chrome 5

1.5

1

The results for the small scenario were:

Total download times (seconds)

combined.html

separate.html

IE 6

0.2

0.8

IE 7

0.4

0.4

IE 8

0.4

0.4

Firefox 3

0.6

0.2

Google Chrome 5

0.2

0.2

The moral of this story is to find out which browsers are most used by your target groups, and then experiment with different scenarios using those browsers.

Preventing 304 messages

When running your own tests, keep in mind that the browser will store the JavaScript files in its cache. As a result, if you press Ctrl + F5 to reload the page, some browsers, including Internet Explorer, will send If-Modified-Since and If-None-Match headers to the server. If you haven't changed the files, the server will then respond with a short 304 message indicating that the browser cache is still up to date, instead of a longer 200 message that contains the entire file. This will skew your results.

You can prevent this by running the Fiddler developers proxy (http://www.fiddler2.com/fiddler2). Its filter feature allows you to neutralize the If-Modified-Since and If-None-Match headers, as shown in the following screenshot:

Implementing automatic file combining

If you decide to combine your files, you could do so manually using a simple text editor. However, that would create a maintenance headache. You could also combine the files as part of the build process, but then you would have to modify your script tags as well.

The best solution would be to have the site combine the files at runtime. It can then also take care of replacing the individual script tags with their counterparts for the combined files.

Here are a number of packages you can add to your site to make this happen:

ASP.NET ScriptManager Control

Compression (deflate) and HTML, CSS, JS Minification in ASP.NET

Combres 2.0

FileCombine

Because CSS files can also benefit from being combined, these packages also support combining CSS files, except for the ASP.NET ScriptManager Control.

ASP.NET ScriptManager Control

You include the ASP.NET ScriptManager control on your pages to use ASP.NET AJAX controls, such as UpdatePanel. It automatically generates the script tags for the JavaScript files needed for those controls. It can also create script tags for other JavaScript files you nominate.

As of ASP.NET 3.5, you can tell the ScriptManager control to combine JavaScript files. It then generates the script tag for the combined file and combines the files on the fly. It also compresses the combined files using GZIP if the browser is not Internet Explorer 6. For example:

This is probably the simplest way to combine script files automatically. However, a drawback of ScriptManager is that it doesn't work with CSS files. Also, it doesn't minify your JavaScript files, and you have to modify your .aspx files to make it work. Finally, the URLs of the combined files contain query strings, which means that you miss out on kernel caching and caching in most proxies.

Compression (deflate) and HTML, CSS, JS Minification in ASP.NET

This package combines JavaScript files and CSS files. It also minifies JavaScript and CSS, and the HTML on the page as well. It compresses your files using the deflate algorithm. And because it uses a custom filter to intercept your HTML to find the script and link tags, there is no need to modify your .aspx files.

On the other hand, it uses script and CSS file URLs that contain query strings, which is not good for caching. And those query strings contain the names of the individual files that make up the combined file, which means that they can get very long. The biggest drawback is that it will break your CSS code if your CSS files are in a separate folder and you use relative image URLs, as in the following code:

background-image: url(../images/chemistry.png);

This is because the combined CSS file is not guaranteed to be served from the same folder, and so the relative URLs no longer refer to the same location.

Combres 2.0

This highly configurable, open source package allows you to organize JavaScript and CSS files into separate resource sets. It automatically detects changes to the files and includes a version number in the combined file URLs, so a browser will never use an outdated file from its cache. It lets you combine, minify, and compress files using either GZIP or deflate. It generates Cache-Control and ETAG headers. It also supports debugging mode, where files are not minified or cached.

The only drawback to this impressive package is that you can't simply drop it into your website. You have to set up a configuration file to make it work, and you have to maintain that file as your site changes.

FileCombine

You'll find the project FileCombine in the folder FileCombine in the downloaded code bundle.

This package combines many of the features of Combres with the ability to simply drop it in your site without having to do configuration:

Combines and minifies both CSS and JavaScript files. To reduce CPU usage, the minified and combined files are kept in server cache. To make sure that you don't serve outdated files from the server cache, a cache entry is removed the moment one of the constituent individual files is updated.

Limits URL length of combined files by using an MD5 hash over the individual URLs as the combined file URL. The alternative of making up the combined file URL by stringing together the URLs of the constituent files can result in an extremely long URL.

The hashing algorithm always produces the same hash for the same constituent file URLs, as long as they are in the same order. That way, if you use the same script and CSS link tags in different pages, the browser needs to load the combined file only once, and can then retrieve it from cache for subsequent pages.

The URL of the combined file includes a shortened version of the last modified time of the youngest constituent file. This stops the browser from using an outdated version from its cache.

To improve proxy caching, no query strings are used in the URLs of combined files.

It assigns far-future Cache-Control headers, so that if the combined files remain unchanged, they stay as long as possible in browser and proxy cache.

It replaces relative url() properties in CSS files with their absolute counterparts, so that they do not break when used in a combined CSS file.

It also detects if the site is in debug mode. If in debug mode, it doesn't minify or combine files, and uses Cache-Control headers that specify immediate expiry from the browser cache.

Limitations of this package:

It considers only script tags and CSS link tags in the head of the page.

It doesn't minify inline script or CSS.

It relies on the server to do GZIP compression. To have your JavaScript and CSS files GZIP-compressed (highly recommended), enable dynamic file compression on the server.

For updates about FileCombine, search Google for "FileCombine".

The installation instructions are in the FileCombine folder in the downloaded code bundle.

Removing unused code

Because JavaScript code is separate from the HTML user interface elements it supports, it is easy to forget to remove code when it is no longer needed. As a result, your JavaScript files may become bloated with unused code.

Here are a number of tools that help identify unused code. Be careful with this— JavaScript is a very dynamic language, making it difficult for a tool to make sure that a piece of code really is unused.

Jsurehttp://aurochs.fr/jsure.html This is another program that statically analyzes your JavaScript. It reports unused functions, variables, and function arguments.

JSCoveragehttp://siliconforks.com/jscoverage/ JSCoverage shows which lines of a program get executed, and which ones are missed. It works by instrumenting the JavaScript code used in web pages. Code coverage statistics are collected while the instrumented JavaScript code is executed in a web browser.

Summary

In this article, we saw how page rendering is blocked by JavaScript <script> tags. This was followed by ways to load other components such as CSS stylesheets and images in parallel with the JavaScript to reduce the overall time taken to load the page fully. We then saw how to load JavaScript files more quickly, including minification, free CDNs, removing unused code, and combining or breaking up JavaScript files.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.