Optimize browser rendering

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.

Use efficient CSS selectors
Avoid CSS expressions
Put CSS in the document head
Specify image dimensions
Specify a character set
Use efficient CSS selectors

Overview

Avoiding inefficient key selectors that match large numbers of elements can speed up page rendering.

Details

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:

Rules with descendant selectors
For example:
Rules with the universal selector as the key
body * {...}
.hide-scrollbars * {...}
Rules with a tag selector as the key
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.

Rules with child or adjacent selectors
For example:
Rules with the universal selector as the key
body > * {...}
.hide-scrollbars > * {...}
Rules with a tag selector as the key
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.

Rules with overly qualified selectors
For example:
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.

Rules that apply the :hover pseudo-selector to non-link elements
For example:
h3: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.

Recommendations

Avoid a universal key selector.
Allow elements to inherit from ancestors, or use a class to apply a style to multiple elements.
Make your rules as specific as possible.
Prefer class and ID selectors over tag selectors.
Remove redundant qualifiers.
These qualifiers are redundant:
ID selectors qualified by class and/or tag selectors
Class selectors qualified by tag selectors (when a class is only used for one tag, which is a good design practice anyway).
Avoid using descendant selectors, especially those that specify redundant ancestors.
For example, the rule body ul li a {...} specifies a redundant body selector, since all elements are descendants of the body tag.
Use class selectors instead of descendant selectors.
For example, if you need two different styles for an ordered list item and an ordered list item, instead of using two rules:
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.

Avoid the :hover pseudo-selector for non-link elements for IE clients.
If you use :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.
Additional resources

For more details on efficient CSS rules with Mozilla, see Writing Efficient CSS for Use in the Mozilla UI.
For complete information on CSS, see the Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. For information on CSS selectors specifically, see Chapter 5.
Back to top

Avoid CSS expressions

Overview

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.

Details

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:

To emulate standard CSS properties supported by other browsers but not yet implemented by IE.
To provide dynamic styling and advanced event handling in a more compact and convenient way than writing full-blown JavaScript-injected styles.
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.
Recommendations

Use standard CSS properties if possible.
IE 8 is fully CSS-standards-compliant; it supports CSS expressions only if run in "compatibility" mode, but it does not support them in "standards" mode. If you do not need to maintain backwards compatibility with older versions of IE, you should convert any instances of expressions used in place of standard CSS properties to their CSS standard counterparts. For a complete list of CSS properties and IE versions that support them, see the MSDN CSS Attributes Index. If you do need to support older versions of IE in which the desired CSS properties are not available, use JavaScript to achieve the equivalent functionality.
Use JavaScript to script styles.
If you are using CSS expressions for dynamic styling, it makes sense to rewrite them as pure JavaScript to both improve performance in IE and get the benefit of supporting the same functionality in other browsers at the same time. In this example given on the MSDN page on Dynamic Properties, a CSS expression is used to center an HTML block whose dimensions can change at runtime, and to re-center that block every time the window is resized:
<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>
Back to top

Put CSS in the document head

Overview

Moving inline style blocks and <link> elements from the document body to the document head improves rendering performance.

Details

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.

Recommendations

As required by the HTML 4.01 Specification (section 12.3), always put external stylesheets in the <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.
Put <style> blocks in the <head> section.
Back to top

Specify image dimensions

Overview

Specifying a width and height for all images allows for faster rendering by eliminating the need for unnecessary reflows and repaints.

Details

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.

Recommendations

Specify dimensions that match those of the images themselves.
Don't use width and height specifications to scale images on the fly. If an image file is actually 60 x 60 pixels, don't set the dimensions to 30 x 30 in the HTML or CSS. If the image needs to be smaller, scale it in an image editor and set its dimensions to match (see Optimize images for details.)
Be sure to specify dimensions on the image element or block-level parent
Be sure to set the dimensions on the <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.
Back to top

Specify a character set