Once resources have been downloaded to the client, the browser still needs to load, interpret, and render HTML, CSS, and JavaScript code. By simply formatting your code and pages in ways that exploit the characteristics of current browsers, you can enhance performance on the client side.
Avoiding inefficient key selectors that match large numbers of elements can speed up page rendering.
As the browser parses HTML, it constructs an internal document tree representing all the elements to be displayed. It then matches elements to styles specified in various stylesheets, according to the standard CSS cascade, inheritance, and ordering rules. In Mozilla's implementation (and probably others as well), for each element, the CSS engine searches through style rules to find a match. The engine evaluates each rule from right to left, starting from the rightmost selector (called the "key") and moving through each selector until it finds a match or discards the rule. (The "selector" is the document element to which the rule should apply.)
According to this system, the fewer rules the engine has to evaluate the better. So, of course, removing unused CSS is an important step in improving rendering performance. After that, for pages that contain large numbers of elements and/or large numbers of CSS rules, optimizing the definitions of the rules themselves can enhance performance as well. The key to optimizing rules lies in defining rules that are as specific as possible and that avoid unnecessary redundancy, to allow the style engine to quickly find matches without spending time evaluating rules that don't apply.
The following categories of rules are considered to be inefficient:
body * {...}
.hide-scrollbars * {...}
ul li a {...}
#footer h3 {...}
* html #atticPromo ul li a {...]
Descendant selectors are inefficient because, for each element that matches the key, the browser must also traverse up the DOM tree, evaluating every ancestor element until it finds a match or reaches the root element. The less specific the key, the greater the number of nodes that need to be evaluated.
body > * {...}
.hide-scrollbars > * {...}
ul > li > a {...}
#footer > h3 {...}
Child and adjacent selectors are inefficient because, for each matching element, the browser has to evaluate another node. It becomes doubly expensive for each child selector in the rule. Again, the less specific the key, the greater the number of nodes that need to be evaluated. However, while inefficient, they are still preferable to descendant selectors in terms of performance.
ul#top_blue_nav {...}
form#UserLogin {...}
ID selectors are unique by definition. Including tag or class qualifiers just adds redundant information that needs to be evaluated needlessly.
:hover
pseudo-selector to non-link elementsh3:hover {...}
.foo:hover {...}
#foo:hover {...}
div.faa :hover {...}
The :hover pseudo-selector on
non-anchor
elements
is known to make IE7 and IE8 slow in some cases*. When a
strict doctype is not used, IE7 and IE8 will ignore :hover
on any
element other than anchors. When a strict doctype is used, :hover
on
non-anchors may cause performance degradation.
* See a bug report at http://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=391387.
body ul li a {...} specifies a
redundant body selector, since all elements
are descendants of the body tag.
ul li {color: blue;}
ol li {color: red;}
You could encode the styles into two class names and use those in your rules; e.g:
.unordered-list-item {color: blue;}
.ordered-list-item {color: red;}
If you must use descendant selectors, prefer child selectors, which at least only require evaluation of one additional node, not all the intermediate nodes up to an ancestor.
:hover pseudo-selector
for non-link elements for IE clients.:hover on non-anchor
elements, test the page in
IE7 and IE8 to be sure your page is usable. If you
find that :hover is
causing performance issues, consider conditionally using a JavaScript onmouseover
event handler for IE clients.
CSS expressions degrade rendering performance; replacing them with alternatives will improve browser rendering for IE users.
Note: This best practices in this section apply only to Internet Explorer 5 through 7, which support CSS expressions. CSS expressions are deprecated in Internet Explorer 8, and not supported by other browsers.
Internet Explorer 5 introduced CSS expressions, or "dynamic properties", as a means of dynamically changing document properties in response to various events. They consist of JavaScript expressions embedded as the values of CSS properties in CSS declarations. For the most part, they are used for the following purposes:
Unfortunately, the performance penalty imposed by CSS
expressions is
considerable, as the browser reevaluates each expression whenever any
event is triggered, such as a window resize, a mouse movement and so
on. The poor performance of CSS expressions is one of the reasons they
are now
deprecated in IE 8. If you have used CSS expressions in your pages, you
should make every effort to remove them and use other methods to
achieve the same functionality.
<div id="oDiv" style="background-color: #CFCFCF; position: absolute;
left:expression(document.body.clientWidth/2-oDiv.offsetWidth/2);
top:expression(document.body.clientHeight/2-oDiv.offsetHeight/2)">Example DIV</div>
Here's an equivalent example using JavaScript and standard CSS:
<style>
#oDiv { position: absolute; background-color: #CFCFCF;}
</style>
<script type="text/javascript">
// Check for browser support of event handling capability
if (window.addEventListener) {
window.addEventListener("load", centerDiv, false);
window.addEventListener("resize", centerDiv, false);
} else if (window.attachEvent) {
window.attachEvent("onload", centerDiv);
window.attachEvent("onresize", centerDiv);
} else {
window.onload = centerDiv;
window.resize = centerDiv;
}
function centerDiv() {
var myDiv = document.getElementById("oDiv");
var myBody = document.body;
var bodyWidth = myBody.offsetWidth;
//Needed for Firefox, which doesn't support offsetHeight
var bodyHeight;
if (myBody.scrollHeight)
bodyHeight = myBody.scrollHeight;
else bodyHeight = myBody.offsetHeight;
var divWidth = myDiv.offsetWidth;
if (myDiv.scrollHeight)
var divHeight = myDiv.scrollHeight;
else var divHeight = myDiv.offsetHeight;
myDiv.style.top = (bodyHeight - divHeight) / 2;
myDiv.style.left = (bodyWidth - divWidth) / 2;
}
</script>
If you are using CSS expressions to emulate CSS properties
that aren't
available in earlier versions of IE, you should provide JavaScript code
for those cases with a version test to disable it for browsers that do
support CSS. For example, the max-width property,
which forces text to wrap around at a certain number of pixels, was not
supported until IE 7. As a workaround, this CSS expression provides
that functionality for IE 5 and 6:
p { width: expression( document.body.clientWidth > 600 ? "600px" : "auto" ); }
To replace the CSS expression with equivalent JavaScript for the IE versions that don't support this property, you could use something like the following:
<style>
p { max-width: 300px; }
</style>
<script type="text/javascript">
if ((navigator.appName == "Microsoft Internet Explorer") && (parseInt(navigator.appVersion) < 7))
window.attachEvent("onresize", setMaxWidth);
function setMaxWidth() {
var paragraphs = document.getElementsByTagName("p");
for ( var i = 0; i < paragraphs.length; i++ )
paragraphs[i].style.width = ( document.body.clientWidth > 300 ? "300px" : "auto" );
</script>
Moving inline style blocks and <link>
elements from the document body to the document head improves rendering
performance.
Specifying external stylesheets and inline style blocks in the
body
of an HTML document can negatively affect the browser's rendering
performance. Browsers block rendering a web page until all external
stylesheets have
been
downloaded. Inline style blocks (specified with the <style>
tag) can cause reflows and shifting of content.
Therefore, it's important to put references to external stylesheets, as
well as inline style blocks, in the head of the
page. By ensuring that stylesheets are downloaded and parsed first, you
can allow
the browser to
progressively render the page.
<head>
section using the in the <link>
tag. Don't use @import.
Also make sure that you specify the stylesheets
in
the correct order with respect to scripts.<style> blocks
in the <head>
section.Specifying a width and height for all images allows for faster rendering by eliminating the need for unnecessary reflows and repaints.
When the browser lays out the page, it needs to be able
to
flow around replaceable elements such as images. It can begin to render
a page even before images are downloaded, provided that it knows the
dimensions to wrap non-replaceable elements around. If no dimensions
are specified in the containing document, or if the dimensions
specified don't match those of the actual images, the
browser will
require a reflow and repaint once the images are downloaded. To prevent
reflows, specify the width and height
of all images, either in the HTML <img>
tag, or in CSS.
<img>
element itself, or a block-level parent. If the parent is not
block-level, the dimensions will be ignored.
Do not set dimensions on an ancestor that is not an immediate parent.Specifying a character set in the HTTP response headers of your HTML documents allows the browser to begin parsing HTML and executing scripts immediately.
HTML documents are sent over the Internet as a sequence of bytes accompanied by character encoding information. Character encoding information is specified in the HTTP response headers sent with the document, or in the HTML markup of the document itself. The browser uses the character encoding information to convert the stream of bytes into characters that it renders on-screen. Because a browser cannot correctly render a page without knowing how to construct the page's characters, most browsers buffer a certain number of bytes before executing any JavaScript or drawing the page, while they search for character set information in the input. (A notable exception is Internet Explorer versions 6, 7, and 8.)
Browsers differ with the respect to the number of bytes buffered and the default encoding assumed if no character set is found. However, once they have buffered the requisite number of bytes and begun to render the page, if they encounter a character set specification that doesn't match their default, they need to reparse the input and redraw the page. Sometimes, they may even have to rerequest resources, if the mismatch affects the URLs of external resources.
To avoid these delays, you should always specify the character encoding in the HTTP response headers. Note that, while it is possible to specify a character set using a meta http-equiv tag, doing so disables the lookahead downloader in Internet Explorer 8. Disabling the lookahead downloader can substantially increase the amount of time it takes to load your page. Microsoft notes: "we continue to strongly recommend that web developers specify the CHARSET in the HTTP Content-Type response header, as this ensures that the performance benefit of the Lookahead Downloader is realized".
For details on browser behavior with respect to the presence/absence of content-type and charset specifications, see: