Part 3 of 3: Reducing Parse Time

In This Tutorial

  1. Introduction
  2. DOM Elements
  3. Image Sizes
  4. Cookie-Free Domains
  5. Cookie Size
  6. JavaScript
  7. CSS
  8. CSS Expressions
  9. Duplicate Content
  10. 404 Not Found
  11. Conclusion

Introduction

This tutorial operates on the presumption that you have read the first two ever-so-interesting parts: Reducing File Size and Reducing Server Calls. The first part, especially, introduces you to the importance of web page optimization. This final chapter deals with minimizing a web page's parse time - that is, the time it takes for the browser to convert the document from markup to visual elements. This concludes the sequential steps of web page creation: creating the files in a manner to reduce their size, retrieving the files in a manner to reduce server stress, and finally loading and displaying the files in a manner that occurs promptly.

In summation, you will learn why you should and/or how to reduce the number of DOM elements on the page, include the width and height attribute of images, use cookie-free domains, reduce cookie size, place JavaScript at the bottom of the page, place CSS at the top of the page, avoid CSS expressions, remove duplicate JavaScript and CSS, and avoid HTTP 404 errors.

Reduce the Number of DOM Elements

While elements take a bit of time themselves to display, the main reason you'll want to avoid overusing them is JavaScript. When returning a list of elements (e.g. document.getElementsByTagName("td")), JavaScript must search the entire DOM. The more elements there are, the longer it takes to generate and return the list. For this reason, you'll want to decrease your DOM.

This is most applicable to pages that use markup for styling, as opposed to CSS. Here is a before-and-after example of using CSS to decrease your DOM:

<font color="ff0000"><b><i>This text is important.</i></b></font>
<strong style="color: #ff0000; font-style: italic; font-weight: bold;">This text is important.</span>

This one sentence now has one third as many elements, essentially takes one third as long for JavaScript to read the DOM tree.

Pages often contain inexcusably empty elements. This is most commonly seen when content is automatically generated. If a container has no content to display, it will still display the container elements, but just leave them empty.

echo '<div id="advertisement">';
if ($user['account_type'] == 'free')
	echo '<a href="/premium" title="Purchase Premium">Upgrade</a>';
echo '</div>';

The above code will still display the #advertisement element even if the user isn't under a free account. JavaScript must still acknowledge this element in the DOM, thus increasing its scanning time. Save some bandwidth and save some parse time: remove elements that don't need to exist.

Include the Width/Height in Images

This is a tip that not even YSlow mentions, but it definitely should. When displaying inline images (<img />), set the height and width attributes. While this may immediately sound contradictory to the previous rule of not scaling images in HTML, the key difference is that we're not scaling.

Take this image as an example. The dimensions are 306px wide by 37px tall.

Charles Stover
<img alt="Charles Stover" height="37" src="banner.png" width="306" />

When an image is displayed inline, the browser doesn't know what size the image is going to be until after it has downloaded. It therefore can't allocate the appropriate space in pixels in which it is displayed. It can only add an arbitrarily sized placeholder until the image has loaded, after which it can calculate the appropriate size, then set the image's dimensions correctly.

Now let's break that down. Let's say you include the above image without the height and width values. What will the browser do? Most will likely put something along the lines of a 32×32 empty box. What will the user see? The 32×32 box suddenly pop into a 306×37 image. This will cause other content on the page to shift. Content below the image will be pushed down and content to the right of the image will be pushed across. By specifying the height and width pre-load, the browser will allocate the image's size before the image is loaded. Thus, post-load, other content on the page will still be in the same place it was.

This just gives the appearance of faster loading. However, when no width or height are given to the element, the browser has to read the dimension data from the image. That takes time. For both parse time and aesthetic necessity, use the width and height attributes. With PHP, the getimagesize function is a godsend.

Put JavaScript at the Bottom

An unfortunate attribute of JavaScript (namely due to its use of document.write) is that when an external script is being called, the browser won't render anything else until that JavaScript file has finished downloading and parsing.

<div id="header">Welcome to my website!</div>
<script src="all-my-functions.js" type="text/javascript"></script>
<div id="content">
	<p>This is what my users came to my website to view!</p>
</div>

In this exampel, the user won't see any of div#content until after the all-my-functions.js file has finished downloading. This is to prevent any DOM errors. If the browser went ahead and parsed div#content before loading all-my-functions.js, it would be in a bit of a pickle if it were to find out that the JavaScript file contains something along the lines of document.write("<div id=\"wrapper\">");. div#content would have to be erased, the DOM for div#wrapper created, and div#content recreated. The browser thus waits for JavaScript files to finish loading before parsing the rest of the markup on the page.

For this reason, if it's at all possible to move JavaScript files to the bottom of the page (especially files that don't run until window.onload), the page will load with much less lag for the user. If the JavaScript provides necessary functionality, it may be an exception. The user shouldn't have to wait for the page to load to use the functionality of an element in the header. Sites that move header-function JavaScript to the bottom of the page leave the user helpless until the page has finished loading, which can take quite a while if it contains a lot of content. In an era of instant information, anything on which your user has to wait more than a second is taking too long to access.

In these rare instances, it is best to use as little JavaScript as possible to produce the functionality. Instead of using an all-my-functions.js file, use a header-functions.js file at the top of the page and put the rest that aren't necessary at the bottom of the page.

Put CSS at the Top

Similar to including the width and height attributes of an image, placing CSS at the top of the page provides the appearance of a faster load, which is all too important to the user.

This allows the browser to stylize the elements as they're created. When div#header is created, the user will see the entirety of div#header, instead of just the plain text, unstyled version of it. To the user, the physical appearance of the element is just as much a signifier of its full-load as the content within it.

Also to note, if the browser receives CSS for an element after it has loaded, the browser has to redraw that element, which takes some times (the more CSS, the longer it takes). While JavaScript is often used to change the background color, background image, or foreground color when an event is triggered, little is heard of redraw time due to the fact that it is almost instantaneous. On larger elements, however, we're not talking about simple colors. Think of the float, position, display, border, padding, margin, etc. attributes. All of these require quite a lot of information to be redrawn, especially when mixing and matching them.

Most relevant and important to this fact is that Internet Explorer will refuse to draw an element if there's a chance that it will be restyled later in the document (a stylesheet that appears toward the end of the document). Thus, in the browser, the user will simply see a blank page until the stylesheet has loaded. To the user, it will seem as if they have yet to even make the connection to your website, let alone started to download content.

Avoid CSS Expressions

Luckily, CSS expressions are not widely used. They are an Internet Explorer feature that allows JavaScript to parse within CSS in order to generate values for attributes.

body {
	background-color: expression(typeof(theme) != "undefined" && theme == 2 ? "#ffffff" : "#000000");
	color: expression(typeof(theme) != "undefined" && theme == 2 ? "#000000" : "#ffffff");
}

This code snippet will allow the user to switch between a white and black theme by merely changing the theme variable.

While it sounds unbelievably useful at first, one must take into consideration just how often these expressions are evaluated. The browser doesn't know to wait for the theme variable to change, nor is it limited to such a a simple if statement in the expression. Therefore, it must evaluate these expressions periodically, so that when the theme variable is changed, the CSS will change seamlessly.

Unfortunately for users, this means it evaluates these expressions with nearly every event: when a user resizes the window, blurs or focuses the window, scrolls the page, or even moves their mouse. This takes up a lot of processing time. Change the CSS via JavaScript with the event itself instead of via the expression function in CSS.

Before (CSS expression):

<!-- CSS -->
<style type="text/css">

body {
	background-color: expression(typeof(theme) != "undefined" && theme == 2 ? "#ffffff" : "#000000");
	color: expression(typeof(theme) != "undefined" && theme == 2 ? "#000000" : "#ffffff");
}

</style>

<!-- HTML -->
<form action="index.php" method="post">
	<select name="theme" onchange="var theme = this.options[this.selectedIndex].value;">
		<option value="1">-- Select a Theme --</option>
		<option value="1">Black</option>
		<option value="2">White</option>
	</select>
</form>

After (JS event):

<!-- CSS -->
<style type="text/css">

body {
	background-color: #000000;
	color: #ffffff;
}

</style>

<!-- JavaScript -->
<script type="text/javascript">

var themes = [
		// [background color, foreground color]
		["#000000", "#ffffff"], // theme = 1
		["#ffffff", "#000000"]  // theme = 2
	],
	setTheme = function(id) {
		document.body.style.backgroundColor = themes[id][0];
		document.body.style.color = themes[id][1];
	};

</script>

<!-- HTML -->
<form action="index.php" method="post">
	<select name="theme" onchange="setTheme(this.options[this.selectedIndex].value);">
		<option value="1">-- Select a Theme --</option>
		<option value="1">Black</option>
		<option value="2">White</option>
	</select>
</form>

Thus, the colors are only evaluated when they're supposed to be evaluated. The user doesn't spend countless, repetitive milliseconds calculating something that isn't going to affect anything.

Remove Duplicate JavaScript and CSS

]

This happens mostly via carelessness, but especially in larger projects. Say, for example, only some pages need the jQuery library. jquery.js is included on pages that have image sliders, and jquery.js is included on pages that use media players. Server-side, it's rather simplified:

if ($content['has_ajax'])
	use_script('ajax.js');

if ($content['has_image_slider'])
	use_script('jquery.js');

if ($content['has_lightbox'])
	use_script('lightbox.js');

if ($content['has_media_player'])
	use_script('jquery.js');

Now if the content has both an image slider and a media player, there's a predicament. The page contains two copies of jquery.js! It's not noticeable on load, since it renders the same, so many developers may not realize they're using the same script twice.

This has obvious downsides. Not only may the browser make extra HTTP requests to the server (which can be alleviated with cache), but it will evaluate the JavaScript (or CSS) twice. This, of course, will make the script take twice as long to parse as it should, since it will be parsed twice.

This is valid for more than the same file - the same function doesn't need to be defined in multiple files either. Be extra careful with external scripts, especially on larger projects and projects with multiple developers (who may not be aware that another developer has already included the script or stylesheet).

Avoid HTTP 404 Not Found Errors

While a 404 error is useful for directing a lost user from a missing HTML page to their desired page, it is entirely useless the vast majority of the time in CSS, JavaScript, and image files. Make sure these files have special 404 handlers. There is no need for the user to spend time downloading a 5 kilobyte HTML file that isn't going to display since it's being called from within an <img /> element! While a alert("JavaScript File Not Found") error may be useful to a developer, it will only serve to confuse your user. It may be most useful to simply send blank files for 404 errors of external content.

Conclusion

Alas, you should now be an expert in web page optimization. It's unfortunately a rare talent amongst web programmers. If I were in any position to give you an award for your valiant efforts to go above and beyond what is required for most programming jobs, I would.

Alternatively, you could give this tutorial a share on Twitter.