ASP.Net Site Performance: Reducing Page Load Time

Exclusive offer: get 50% off this eBook here
ASP.NET Site Performance Secrets

ASP.NET Site Performance Secrets — Save 50%

Simple and proven techniques to quickly speed up your ASP.NET website

$35.99    $18.00
by Matt Perdeck | October 2010 | .NET Web Development

In the previous article, ASP.Net Site Performance: Improving JavaScript Loading, we saw how JavaScript files can block rendering of the page while they are being loaded and executed, and how to load JavaScript in parallel with other resources.

In this article by Matt Perdeck, author of ASP.NET Site Performance Secret, we will see how to load JavaScript code on demand, and techniques to load JavaScript without blocking page rendering.

 

ASP.NET Site Performance Secrets

ASP.NET Site Performance Secrets

Simple and proven techniques to quickly speed up your ASP.NET website

  • Speed up your ASP.NET website by identifying performance bottlenecks that hold back your site's performance and fixing them
  • Tips and tricks for writing faster code and pinpointing those areas in the code that matter most, thus saving time and energy
  • Drastically reduce page load times
  • Configure and improve compression – the single most important way to improve your site's performance
  • Written in a simple problem-solving manner – with a practical hands-on approach and just the right amount of theory you need to make sense of it all
        Read more about this book      

(For more resources on ASP.Net, see here.)

Approach: Loading JavaScript on demand

The JavaScript code for a page falls into two groups—code required to render the page, and code required to handle user interface events, such as button clicks. The code to render the page is used to make the page look better , and to attach event handlers to for example, buttons.

Although the rendering code needs to be loaded and executed in conjunction with the page itself, the user interface code can be loaded later, in response to a user interface event, such as a button click. That reduces the amount of code to be loaded, and therefore the time rendering of the page is blocked. It also reduces your bandwidth costs, because the user interface code is loaded only when it's actually needed.

On the other hand, it does require separating the user interface code from the rendering code. You then need to invoke code that potentially hasn't loaded yet, tell the visitor that the code is loading, and finally invoke the code after it has loaded.

Let's see how to make this all happen.

Separating user interface code from render code

Depending on how your JavaScript code is structured, this could be your biggest challenge in implementing on-demand loading. Make sure the time you're likely to spend on this and the subsequent testing and debugging is worth the performance improvement you're likely to gain.

A very handy tool that identifies which code is used while loading the page is Page Speed, an add-on for Firefox. Besides identifying code that doesn't need to be loaded upfront, it reports many speed-related issues on your web page.

Information on Page Speed is available at
http://code.google.com/speed/page-speed/.

OnDemandLoader library

Assuming your user interface code is separated from your render code, it is time to look at implementing actual on-demand loading. To keep it simple, we'll use OnDemandLoader, a simple low-footprint object. You'll find it in the downloaded code bundle in the folder OnDemandLoad in the file OnDemandLoader.js.

OnDemandLoader has the following features:

  • It allows you to specify the script, in which it is defined, for each event-handler function.
  • It allows you to specify that a particular script depends on some other script; for example Button1Code.js depends on library code in UILibrary1.js. A script file can depend on multiple other script files, and those script files can in turn be dependent on yet other script files.
  • It exposes function runf, which takes the name of a function, arguments to call it with, and the this pointer to use while it's being executed. If the function is already defined, runf calls it right away. Otherwise, it loads all the necessary script files and then calls the function.
  • It exposes the function loadScript, which loads a given script file and all the script files it depends on. Function runf uses this function to load script files.
  • While script files are being loaded in response to a user interface event, a "Loading..." box appears on top of the affected control. That way, the visitor knows that the page is working to execute their action.
  • If a script file has already been loaded or if it is already loading, it won't be loaded again.
  • If the visitor does the same action repeatedly while the associated code is loading, such as clicking the same button, that event is handled only once.
  • If the visitor clicks a second button or takes some other action while the code for the first button is still loading, both events are handled.

A drawback of OnDemandLoader is that it always loads all the required scripts in parallel. If one script automatically executes a function that is defined in another script , there will be a JavaScript error if the other script hasn't loaded yet. However, if your library script files only define functions and other objects, OnDemandLoader will work well.

Initializing OnDemandLoader

OnDemandLoading.aspx in folder OnDemandLoad in the downloaded code bundle is a worked-out example of a page using on-demand loading. It delays the loading of JavaScript files by five seconds, to simulate slowly loading files. Only OnDemandLoader.js loads at normal speed.

If you open OnDemandLoading.aspx, you'll find that it defines two arrays—the script map array and the script dependencies array. These are needed to construct the loader object that will take care of the on-demand loading.

The script map array shows the script file, in which it is defined, for each function:

var scriptMap = [
{ fname: 'btn1a_click', src: 'js/Button1Code.js' },
{ fname: 'btn1b_click', src: 'js/Button1Code.js' },
{ fname: 'btn2_click', src: 'js/Button2Code.js' }
];

Here, functions btn1a_click and btn1b_click live in script file js/Button1Code. js, while function btn2_click lives in script file js/Button2Code.js.

The second array defines which other script files it needs to run for each script file:

var scriptDependencies = [
{
src: '/js/Button1Code.js',
testSymbol: 'btn1a_click',
dependentOn: ['/js/UILibrary1.js', '/js/UILibrary2.js']
},
{
src: '/js/Button2Code.js',
testSymbol: 'btn2_click',
dependentOn: ['/js/UILibrary2.js']
},
{
src: '/js/UILibrary2.js',
testSymbol: 'uifunction2',
dependentOn: []
},
{
src: '/js/UILibrary1.js',
testSymbol: 'uifunction1',
dependentOn: ['/js/UILibrary2.js']
}
];

This says that Button1Code.js depends on UILibrary1.js and UILibrary2.js. Further, Button2Code.js depends on UILibrary2.js. Further, UILibrary1.js relies on UILibrary2.js, and UILibrary2.js doesn't require any other script files.

The testSymbol field holds the name of a function defined in the script. Any function will do, as long as it is defined in the script. This way, the on-demand loader can determine whether a script has been loaded by testing whether that name has been defined.

With these two pieces of information, we can construct the loader object:

<script type="text/javascript" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="js/OnDemandLoader.js">
</script>
var loader = new OnDemandLoader(scriptMap, scriptDependencies);

Now that the loader object has been created, let's see how to invoke user interface handler functions before their code has been loaded.

Invoking not-yet-loaded functions

The point of on-demand loading is that the visitor is allowed to take an action for which the code hasn't been loaded yet. How do you invoke a function that hasn't been defined yet? Here, you'll see two approaches:

  • Call a loader function and pass it the name of the function to load and execute
  • Create a stub function with the same name as the function you want to execute, and have the stub load and execute the actual function

Let's focus on the first approach first.

The OnDemandLoader object exposes a loader function runf that takes the name of a function to call, the arguments to call it with, and the current this pointer:

function runf(fname, thisObj) {
// implementation
}

Wait a minute! This signature shows a function name parameter and the this pointer, but what about the arguments to call the function with? One of the amazing features of JavaScript is that can you pass as few or as many parameters as you want to a function, irrespective of the signature. Within each function, you can access all the parameters via the built-in arguments array. The signature is simply a convenience that allows you to name some of the arguments.

This means that you can call runf as shown:

loader.runf('myfunction', this, 'argument1', 'argument2');

If for example, your original HTML has a button as shown:

<input id="btn1a" type="button" value="Button 1a"
onclick="btn1a_click(this.value, 'more info')" />

To have btn1a_click loaded on demand, rewrite this to the following (file OnDemandLoading.aspx):

<input id="btn1a" type="button" value="Button 1a"
onclick="loader.runf('btn1a_click', this, this.value,
'more info')" />

If, in the original HTML, the click handler function was assigned to a button programmatically as shown:

<input id="btn1b" type="button" value="Button 1b" />
<script type="text/javascript">
window.onload = function() {
document.getElementById('btn1b').onclick = btn1b_click;
}
</script>

Then, use an anonymous function that calls loader.runf with the function to execute:

<input id="btn1b" type="button" value="Button 1b" />
<script type="text/javascript">
window.onload = function() {
document.getElementById('btn1b').onclick = function() {
loader.runf('btn1b_click', this);
}
}
</script>

This is where you can use the second approach—the stub function. Instead of changing the HTML of your controls, you can load a stub function upfront before the page renders (file OnDemandLoading.aspx):

function btn1b_click() {
loader.runf('btn1b_click', this);
}

When the visitor clicks the button, the stub function is executed. It then calls loader.runf to load and execute its namesake that does the actual work, overwriting the stub function in the process.

This leaves behind one problem. The on-demand loader checks whether a function with the given name is already defined before initiating a script load. And a function with that same name already exists—the stub function itself.

 

The solution is based on the fact that functions in JavaScript are objects. And all JavaScript objects can have properties. You can tell the on-demand loader that a function is a stub by attaching the property "stub":

btn1b_click.stub = true;

To see all this functionality in action, run the OnDemandLoading.aspx page in folder OnDemandLoad in the downloaded code bundle. Click on one of the buttons on the page, and you'll see how the required code is loaded on demand. It's best to do this in Firefox with Firebug installed, so that you can see the script files getting loaded in a Waterfall chart.

Preloading

Now that you have on-demand loading working, there is one more issue to consider: trading off bandwidth against visitor wait time.

Currently, when a visitor clicks a button and the code required to process the click hadn't been loaded, loading starts in response to the click. This can be a problem if loading the code takes too much time.

An alternative is to initiate loading the user interface code after the page has been loaded, instead of when a user interface event happens. That way, the code may have already loaded by the time the visitor clicks the button; or at least it will already be partly loaded, so that the code finishes loading sooner. On the other hand, this means expending bandwidth on loading code that may never be used by the visitor.

You can implement preloading with the loadScript function exposed by the OnDemandLoader object. As you saw earlier, this function loads a JavaScript file plus any files it depends on, without blocking rendering. Simply add calls to loadScript in the onload handler of the page, as shown (page PreLoad.aspx in folder OnDemandLoad in the downloaded code bundle):

<script type="text/javascript">
window.onload = function() {
document.getElementById('btn1b').onclick = btn1b_click;

loader.loadScript('js/Button1Code.js');
loader.loadScript('js/Button2Code.js');
}
</script>

You could preload all your user interface code, or just the code you think is likely to be needed.

Now that you've looked at the load on demand approach, it's time to consider the last approach—loading your code without blocking page rendering and without getting into stub functions or other complications inherent in on-demand loading.

ASP.NET Site Performance Secrets Simple and proven techniques to quickly speed up your ASP.NET website
Published: October 2010
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on ASP.Net, see here.)

Approach: Loading Javascript without blocking

The idea behind this approach is to load all (or almost all) script files without blocking rendering of the page. That puts the rendered page sooner in front of the visitor.

There are a couple of ways to achieve this, each trading off more work for a better visitor experience:

  • Moving all <script> tags to the end of the page
  • Separating user interface code and render code
  • Introducing page loading indicator
  • Loading code in parallel with the page

Let's go through each of them.

Moving all <script> tags to the end of the page

On a basic level, loading JavaScript code without blocking page rendering is really easy to accomplish—simply take your script tags out of the head of the page, and move them to the end of the body. That way, the page will have rendered before the script tags have a chance to block anything.

Page ScriptAtEnd.aspx in folder LoadJavaScriptWithoutBlocking in the downloaded code bundle has an example for this. Similar to the test site you used in the previous section, to simulate a slowly loading JavaScript file, this site delays JavaScript files by five seconds; just make sure you run it in IIS 7.

The example page has both render code and user interface code (ScriptAtEnd.aspx):

<script type="text/javascript" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="js/Code.js"></script>
<script type="text/javascript">
// Execute render code
beautify();
// Attach event handlers for user interface
attachEventHandlers();
</script>

In this example, the render code simply makes the background of the page yellow, and turns the caption of the middle button yellow as well. That may be very simplistic, but that makes it perfectly clear whether the render code is executed.

When you load ScriptAtEnd.aspx, you'll see the "raw" version of the page until the code finishes loading and changes the page's appearance. That's not necessarily what you want.

This brings us to the next iteration.

Separating user interface code and render code

The code (if there is any) required to render the page tends to be much smaller than that required to handle user interface events. So, to prevent showing the raw page to visitors, it can make sense to separate out the render code, and load that upfront. It will block rendering of the page while it is loading, but in this case, this is precisely what we want.

Use the Page Speed add-on for Firefox to figure out which JavaScript functions are not needed to render the page.

After you've separated the code, you'll wind up with something similar to the following (page SeparateScripts.aspx):

<head runat="server">
<script type="text/javascript" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="js/Beautify.js"></script>
</head>
<body>
... page contents
<script type="text/javascript">
beautify(); // run code that helps render the page
</script>
<script type="text/javascript" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="js/UICode.js"></script>
<script type="text/javascript">
attachEventHandlers();
</script>
</body>

The render code is now loaded in the head of the page, so that it blocks rendering the rest of the page. It is executed at the end of the page by calling function beautify; otherwise, there is no page content to work with. Only then the user interface code is loaded, so that it doesn't block the call to beautify.

If you run SeparateScripts.aspx, you should no longer see the raw version of the page. However, if you click any of the buttons while the user interface code is being loaded, nothing happens. This may cause your visitors to think that your site is broken, and move on to some other site, such as your competitor's.

If you look closely, you'll see that the browser shows that the page is busy while the user interface code is loading. However, you can't rely on your visitors to see this. So, let's add a better "page loading" indicator.

Introducing page loading indicator

Introducing a page loading indicator consists of creating the indicator itself, and introducing code to make it go away once all the code has completed loading.

The page loading indicator itself can be any <div> tag with a "Loading ..." text. The code below fixes the indicator just below the browser window, so that it stays there even if the visitor scrolls the page (PageLoadingIndicator.aspx):

<div id="pageloading" style="position:fixed; top: 10px;
left: 50%;" >Loading ...</div>

After all the code is loaded and you've attached the event handlers, make the loading indicator disappear by setting its display style to none:

<script type="text/javascript">
attachEventHandlers();
document.getElementById('pageloading').style.display = 'none';
</script>

Alternatively, or in addition, you could disable all input elements after the page is rendered. This dims the captions of all buttons and prevents them from being "depressed" when clicked (PageLoadingIndicator.aspx):

<script type="text/javascript">
beautify();

function disableButtons(disable) {
var inputTags = document.getElementsByTagName('input');
var inputTagsLen = inputTags.length;
for (var i = 0; i < inputTagsLen; i++) {
inputTags[i].disabled = disable;
}
}
disableButtons(true); // Disable all input elements
</script>

Then after the code is loaded, re-enable them again (PageLoadingIndicator.aspx):

<script type="text/javascript">
attachEventHandlers();
document.getElementById('pageloading').style.display = 'none';

disableButtons(false); // Re-enable all input elements
</script>

If you run JavaScript code that changes the color of button captions while they are disabled, Firefox, Google Chrome, and Safari will apply the color right away, thereby removing the "disabled" look. Internet Explorer, however, changes only the caption color after the buttons have been re-enabled.

Loading code in parallel with page

In the solutions you've seen so far, loading of the user interface code is initiated after the page has loaded and rendered.

However, if the HTML of your page takes a long time to load, you will want to start loading at the beginning of the page, so that the code loads in parallel with the HTML.

You can achieve this with the OnDemandLoader object that you saw in the previous section. You can get it to load one or more sets of script files while the page itself is loading, and to call a function when each set is done. Finally, it exposes an onallscriptsloaded event that fires when all script files have loaded, which can be used to remove the page loading indicator.

This solution is in page ParallelLoading.aspx, folder LoadJavaScriptWithoutBlocking in the downloaded code bundle. It breaks into the following parts:

  • Initialize the loader object
  • Start loading the code
  • Ensure that the code runs after the page is rendered

Let's go through each part.

Initializing the loader object

The first step is prepare two pieces of information: the script map array, showing for each function the script file in which it is defined. And the script dependencies array, showing for each script file which other script files it depends on.

Here, the script map contains the two functions you've already seen: attachEventHandlers to attach the event handlers after the user interface code has loaded, and beautify to execute the render code:

var scriptMap = [
{ fname: 'attachEventHandlers', src: 'js/UICode.js' },
{ fname: 'beautify', src: 'js/Beautify.js' }
];

Also, list both script files in the array with dependencies:

var scriptDependencies = [
{
src: 'js/UICode.js',
testSymbol: 'attachEventHandlers',
dependentOn: []
},
{
src: 'js/Beautify.js',
testSymbol: 'beautify',
dependentOn: []
}
];

If you need to load additional script files, list them in the dependentOn array, along the following lines:

{
src: 'js/UICode.js',
testSymbol: 'attachEventHandlers',
dependentOn: ['js/UILibrary1.js', 'js/UILibrary2.js',
'js/UILibrary3.js']
}

Finally, create the loader object:

<script type="text/javascript" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="js/OnDemandLoader.js">
</script>
var loader = new OnDemandLoader(scriptMap, scriptDependencies);

With the loader object established, it is now time to start loading the JavaScript code.

Loading the code while the page is loading

You want the code to load while the page is loading, which means that the JavaScript that initiates the loading needs to sit towards the top of the page. Here is what it looks like (ParallelLoading.aspx):

<body>
<div id="pageloading" ...>Loading ...</div>
<script type="text/javascript">
loader.onallscriptsloaded = function() {
document.getElementById('pageloading').style.display =
'none';
disableButtons(false);
}

loader.runf('beautify', null);
loader.runf('attachEventHandlers', null);
</script>

If the onallscriptsloaded property on the OnDemandLoader object is set to a function, the object runs that function when it finds that all script files in the dependencies list have been loaded. That feature is used here to hide the "Loading" box and re-enable the buttons after all the script files have been loaded.

You came across loader.runf in the Load on demand section. It tells the loader to make sure that all code required to run the beautify and attachEventHandlers functions has been loaded, and to call those functions once the code has been loaded.

Ensuring that code runs after the page is rendered

There is one last problem to solve: the script files may finish loading before the page itself finishes rendering. This can happen when the script files are in browser cache, left there by another page. The problem is that if the code runs before the page is rendered, it may fail to properly update the appearance of the page, or to attach event handlers to all user interface elements because the page isn't fully there yet.

The way to solve this is to call the beautify and attachEventHandlers functions not only after they have been loaded, but also after the page has finished rendering. That way, you're sure that these functions will be executed properly, even if the script files were quick to load. You do need a try-catch when you call the functions after the page has finished rendering, in case their code hasn't been loaded yet:

try { attachEventHandlers(); } catch (err) { }
try { beautify(); } catch (err) { }

This means that these functions are called twice—once when the code finishes loading, and once after the page has finished rendering. You don't know in which order this happens. It also means that you need to make sure that the functions do not cause an error message if they run while the page isn't rendered yet.

How do we find out when the page is completely rendered? Here are the usual options:

  • Create a handler for the page's onload event. This is the most natural solution, but here it has a big problem. When the OnDemandLoader object starts loading a script file, it does so by inserting a <script> tag into the DOM, as shown in the following code:
    var se = document.createElement('script');
    se.src = src;
    document.getElementsByTagName('head')[0].appendChild(se);

    This method loads a script file without blocking rendering of the page, except that in Firefox, it blocks the onload event. This means that if the render code loads quickly and the user interface code takes a long time, execution of the render code will still be delayed until the user interface code finishes loading, which is not good.

  • Place a <script> tag at the end of the page containing calls to attachEventHandlers and beautify. Unfortunately, Firefox not only blocks onload, but also all script tags until all the code is loaded.
  • Place an invisible element at the very end of the page, and periodically check whether that element is rendered. If it is, the whole page will have rendered. This slightly pollutes the HTML and couples the invisible element and the polling code because they have to refer to the same ID.

You could make the first two options work by loading the JavaScript code asynchronously using XMLHttpRequest instead of inserting a <script> tag in the DOM. However, that would stop you from loading script files from any host but the one used by your site. For example, you then couldn't load the jQuery library from the Google CDN.

So in this example, we'll use the third method, based on polling for the end of page rendering.

To implement the polling solution, first place an invisible element at the very end of the page:

  <div id="lastelement" style="display: none;"></div>
</body>

Then run the polling code at the beginning of the page, in the same script block where you started loading the JavaScript code:

function pollpage() {
var lastelement = document.getElementById('lastelement');
if (lastelement == null) {
setTimeout("pollpage();", 100);
return;
}

try { attachEventHandlers(); } catch (err) { }
try { beautify(); } catch (err) { }

if (document.getElementById('pageloading').
style.display != 'none') {
disableButtons(true);
}
}
pollpage();

The function pollpage first checks whether the element with ID lastelement exists. If not, the page hasn't finished rendering yet, and so it calls setTimeout to have itself called again 100 milliseconds from now.

Otherwise, the page has rendered, and the function calls attachEventHandlers and beautify. Now that all the buttons will have rendered, this is also a good time to disable all the buttons. However, if the JavaScript code has already loaded, you obviously don't want to do that. So it checks whether the "Loading" box has already been made invisible by the OnDemandLoader object.

Finally, the code calls the pollpage function to start polling.

All this is expressed in the following flowchart:

ASP.NET Site Performance Secret

That concludes the four approaches to improving JavaScript loading. We'll now look at two related topics: improving ad loading and improving loading of CSS files.

Improving ad loading

If you use an ad network such as DoubleClick or Google AdWords, they will have given you code to place on your pages along the following lines:

<script src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://adserver.js?....."></script>

This loads some JavaScript from the ad server, which then places the actual ad on your site. Easy.

Normally, this works fine. However, the ad server is slow at times. The problem is that while the browser is waiting for the ad server, it holds off rendering the page below the ad. If there is a long delay, this will not look good.

You could prevent this by loading the ads in iframes. However, this will prevent your ad slots from showing variable-sized ads. It also creates a big empty space on your page if the ad fails to load.

A neat way to solve this problem is to load the ads after the entire page is rendered, and then move the ads to their ad slots.

In this approach, you place an empty <div> tag at the spot where you want an ad to appear. You can give it the size of the eventual ad (if it has fixed size and you don't mind the empty space on your page) and a progress image to make it look nice (page AdDoesNotDelayPage.aspx, folder AdLoad in the downloaded code bundle):

<div id="ad" style="width: 486px; height: 60px; background:
#ffffff url('/images/throbber.gif') no-repeat center;">
</div>

Then at the end of the page, you place the ad loading script within its own <div> tag. Make that tag invisible (display value is none) so that your visitors don't see it while the ad is loading:

<script type="text/javascript">
var scriptloaded = false;
var divloaded = false;
function adloaded() {
...
}
</script>

<div id="tempad" style="display:none">
<script type="text/javascript" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://adserver.js?....."
onload="scriptloaded=true; adloaded()"
onreadystatechange="if (this.readyState=='complete') {
scriptloaded=true; adloaded(); }">
</script>
</div>
<script type="text/javascript">
divloaded = true;
adloaded();
</script>

The function adloaded() will move the <div> to the actual ad slot. But before that can be done, not only the script, but also the <div> needs to have loaded. Otherwise, there will be a JavaScript error when adloaded tries to move it.

Finding out whether the script is loaded means adding an onreadystatechange handler, used by Internet Explorer, and an onload handler, used by all other browsers. Finding out whether the <div> is loaded, means simply add a JavaScript block after the <div>. If both the variables scriptloaded and divloaded are true, the <div> is ready to be moved.

Finally, implement the adloaded function:

function adloaded() {
if ((!scriptloaded) || (!divloaded)) { return; }

var et = document.getElementById("tempad");
et.parentNode.removeChild(et);
var d = document.getElementById("ad");
d.appendChild(et);
et.style.display = "block";
}

This finds the <div> holding the script (with the ID tempad) and detaches it from its parent, so that it won't show up at the bottom of the page. It then finds the <div> marking the ad slot where you want to actually show the ad (with the ID ad). Finally, it appends the <div> with your ad as a child to the empty ad slot and changes its display property to block to make it visible. Done!

Improving CSS Loading

Just as JavaScript files, CSS files also block rendering of the page. This way, the visitor isn't confronted by the page in its unstyled form.

You can see this behavior by running the website in folder CssBlocksRendering in the downloaded code bundle. This test has a single page that loads a single CSS file. Generation of the CSS file on the server is delayed by five seconds, using the HTTP Module DelayModule in its App_Code folder. When you open the page, you'll find that the window stays blank for five seconds.

CSS files also block the execution of JavaScript files. This is because the JavaScript may refer to definitions in the CSS files.

One way to reduce this issue is to load your CSS files as quickly as possible. You can achieve that in the following ways:

  • Using techniques used with images such as caching, parallel downloads, and using a CDN.
  • Using GZIP compression.
  • Combining or breaking up CSS files.
  • Minifying the CSS.
  • Removing unused CSS lines.

A sixth option is to load your CSS without blocking rendering.

Minifying CSS

Minifying CSS is very similar to minifying JavaScript. It involves removing unnecessary white space and comments. It also includes replacing long property values by equivalent shorter ones. For example, the color #ff0000 can be replaced by the shorter red.

The impact of minification on CSS files tends to be fairly modest if compression is enabled on the server. Here are the results for a typical 4-KB CSS file (style1.css in folder Minify in the downloaded code bundle):

 

Minified Reduction through minification
No Yes
GZIP No 4.0 KB 2.6 KB 35 percent
Compressed Yes 1.3 KB 1.2 KB 8 percent

 

There are many tools that will minify a CSS file for you, including the following:

  • Microsoft Ajax Minifier
    http://www.asp.net/ajaxlibrary/AjaxMinDocumentation.ashx
    This tool minifies JavaScript and CSS files.
  • YUI Compressor
    http://developer.yahoo.com/yui/compressor/
    It is a standalone Java-based JavaScript and CSS minifier.
  • Yahoo! UI Library: YUI Compressor for .NET
    http://yuicompressor.codeplex.com/
    In addition to minifying JavaScript, this .NET port of the YUI Compressor Java project also allows you to minify CSS, like so:
    using Yahoo.Yui.Compressor;
    ...
    string compressedCSS =
    CssCompressor.Compress (uncompressedCSS);

    You can use this to create an HTTP Handler for CSS files, in the same way as you saw earlier for JavaScript files. See folder Minify in the downloaded code bundle for an example.

  • CSSTidy
    http://csstidy.sourceforge.net/
    This tool is an open source CSS parser and optimizer. Its source code available in PHP and C++.

Removing unused CSS selectors

Because CSS styles are separate from the pages where they are used, when bits of HTML are removed, the styles used with that HTML often remain in the CSS. After a while, you may wind up with a lot of unused styles, causing confusion and file bloat.

A good tool that helps you identify unused CSS selectors is the Firefox add-on Dust- Me Selectors. Download it at https://addons.mozilla.org/en-US/firefox/addon/5392/.

After you've installed the add-on, open your site in Firefox. Then, click on Tools | Dust-Me Selectors | Find unused selectors, or press Ctrl + Alt + F. This reads all CSS files used by the page and finds the unused selectors. You could also right-click on the pink broom at the bottom of the Firefox window.

Now, click on Tools | Dust-Me Selectors | View saved data to see the used and unused selectors for each CSS file. There, you also find a button to export the used and unused selectors to CSV files.

CSS files are often shared among multiple pages, and so they may have selectors that are only used on some but not all pages. To make sure you catch all used selectors, navigate through the pages on your site. Dust-Me Selectors will go through each page, and move all the used selectors to the used list. That will greatly weed down your unused list. After you've visited all your pages, consider each selector in the unused list for removal from your CSS files.

If your site has lots of pages, visiting each page would take too much time. However, Dust-Me Selectors can read a site map – click Tools | Dust-Me Selectors | Automation | Spider Sitemap. Site maps tell Google and other search engines where to find your pages, so they are good for SEO. Visit http://www.sitemaps.org for more information.

Loading CSS without blocking rendering

Getting the browser to load your CSS without blocking rendering is not hard. Consider the following line of code:

<link rel="Stylesheet" type="text/css" href="css/style1.css" />

Replace it with JavaScript that creates the link element and inserts it into the DOM (folder LoadCssWithoutBlocking in downloaded code bundle):

<script type="text/javascript">
var scriptElem = document.createElement('link');
scriptElem.type = "text/css";
scriptElem.rel = "Stylesheet";
scriptElem.href = "css/style1.css";
document.getElementsByTagName('head')[0].appendChild(scriptElem);
</script>

When you run this page, you'll see that the page becomes visible to the visitor before the CSS is loaded, and then changes appearance when the CSS finishes loading. This may not be the sort of behavior you want, even if it means a page that renders sooner.

Find out more

Here are some more online resources:

Summary

In this article we discussed loading JavaScript code on demand, and techniques specifically aimed at loading JavaScript without blocking page rendering. We also saw how to load ads from ad networks without allowing them to slow down rendering of your page, and improving the way your page loads CSS stylesheets, including minification and removing unused selectors.


Further resources on this subject:


ASP.NET Site Performance Secrets Simple and proven techniques to quickly speed up your ASP.NET website
Published: October 2010
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

About the Author :


Matt Perdeck

Matt Perdeck has over 20 years experience developing high-performance software systems, ranging from the largest ATM network in the Netherlands to embedded software in advanced Wide Area Networks. He has a B.Sc. in Computer Science from the University of Technology Twente, the Netherlands, and an M.B.A. from the University of Western Australia and Copenhagen Business School. He has worked in Australia, the Netherlands, Slovakia, and Thailand. He has extensive .NET and SQL Server development experience and has written several .NET articles. As an old-school software engineer, he is passionate about making things faster, simpler, and more efficiently.

Books From Packt


ASP.NET 3.5 Application Architecture and Design
ASP.NET 3.5 Application Architecture and Design

ASP.NET MVC 1.0 Quickly
ASP.NET MVC 1.0 Quickly

.NET Compact Framework 3.5 Data Driven Applications
.NET Compact Framework 3.5 Data Driven Applications

BlackBerry Java Application Development
BlackBerry Java Application Development

ColdFusion 9 Developer Tutorial
ColdFusion 9 Developer Tutorial

The 3CX IP PBX Tutorial
The 3CX IP PBX Tutorial

Kentico CMS 5 Website Development: Beginner's Guide
Kentico CMS 5 Website Development: Beginner's Guide

Unity 3D Game Development by Example Beginner's Guide
Unity 3D Game Development by Example Beginner's Guide


Your rating: None Average: 4 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
2
P
8
6
a
a
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software