20 tips for building modern sites while supporting old versions of IE
Over the past few years, we've spent a lot of time talking to web developers and the one thing we hear more than anything else is just how difficult it is to build sites that work well across various browser versions and devices. We've lived that problem ourselves while coding for the jQuery project. So we've compiled a list of our 20 top coding patterns and practices that we've picked-up from attending endless conference sessions and reading hundreds of tutorials. We hope they'll save you some time (and headache) as you build.
Sites don’t need to render the same across all browsers
A common concern among developers is ensuring that their sites render the same across all browsers, including non-modern ones. That’s not always necessary. A better route is to consider progressively enhancing your site by offering a solid working baseline experience to users with non-modern browsers while providing a richer UI to those users with modern browsers.
Start with a solid template to simplify development.
Many of these tips are already built into a project template like HTML5 Boilerplate to save you some time. Those rules work equally well for projects targeted at modern browsers and ones that need older browser support all the way back to IE6.
Focus on stable standards
It’s easy to get excited every time a new and cool feature comes out but some of these may still be experimental with the specification still under development. It’s important to remember that features that are in the early stages of specification development are very likely to change, which could impact your site’s stability and your customer’s experience. By focusing on stable standards, you’re ensuring that your customers will be able to have the experience they’re expecting and your site will be more maintainable.
Use IE Compat Inspector to help migrate your site to standards-based code
Use polyfills and shims sparingly
If you absolutely need to provide the same experience across all browsers, polyfills and shims offer code and markup that can help mimic standards-based APIs and functionality. The key thing to remember is that you need to properly vet the code to ensure it meets your use-case and that you can support it going forward.
Test in multiple browsers while developing.
Although modern browsers are much closer to a single standard than ever before, differences still exist. An occasional multi-browser sanity checkpoint during development can ensure that serious cross-browser issues don't crop up at the last minute – or worse yet, after it's all public. Be sure to look in each browser's console like the F12 Dev Tools in IE to see if any error or warning messages appear. For older browsers like IE7 that don't have a built-in console, you can use Firebug Lite to provide one. Or use a cross-browser hosted testing solution like BrowserStack.
Use a build process with tools to check for errors and minify files.
A good set of build tools such as HTML validators, CSS validators, Uglify, and JSHint, or GruntJS can find latent problems, enforce project code standards, and reduce file size to improve performance. These steps don't have to be a roadblock if your IDE or code-editor supports them. Visual Studio, for example, provides the ability to run external tools and combine/compress script files during its build process.
Always use a standards-based doctype to avoid Quirks Mode.
Start with <!DOCTYPE html>. The modern web has no place for Quirks Mode, which was designed so that mid-1990s web pages would be usable in turn-of-the-century "modern" browsers like IE6 and Firefox 2. Most web pages today end up in Quirks Mode accidentally because of an invalid doctype or extraneous text before the doctype. This can cause strange layout issues that are hard to debug.
Understand the backward-compatibility limits of HTML5 tags.
New HTML5 tags like <section>, <header>, and <footer> improve the semantics of markup, but require a special "shiv" script to run in Internet Explorer 6, 7, and 8 or they won't be recognized. Pages that need to work with these legacy browsers even when scripts are disabled cannot use the new HTML5 tags. Using plain <div> elements and classes is often a safer course of action for those cases.
Put CSS file references at the top of the HTML file.
Putting CSS files in the body can result in the browser showing a blank page until the CSS has loaded. CSS files should go into the head of the HTML document to allow the browser to start fetching them as early as possible.
As with external script references, an inline script requires the browser to stop parsing HTML and can also prevent parallel downloading of other resources on the page. This can seriously slow the initial load of the page and give the user a dreaded "blank-page" experience. Script sprinkled around the markup is also more difficult to maintain.
An example would be <button onclick="validate()">Validate</button>. This practice breaks the clean separation that should exist between markup, presentation, and behavior. Also, if scripts load at the bottom of the file, it is possible for a user to interact with the page and trigger an event that attempts to call a script that isn't loaded yet – causing an error.
Know and use the CSS cascade rules.
Simple id and class selectors are useful, but using them exclusively means the markup gets messier and less reusable with unnecessary ids and classes. Using tag, descendant, child, sibling, and attribute selectors in combination with a small number of ids and classes can keep both the markup and CSS simpler and more general. Avoid the use of the "!important" rule at all costs.
Fully prefix vendor-specific CSS properties to future-proof them.
Solve compatibility problems with valid CSS rules, not CSS parser hacks.
CSS parser hacks are unreliable because browsers can be updated causing these hacks to fail. Instead try adding version-specific classes to the html or body tag that can be used in stylesheet rules. Conditional comments can also be used to include a browser-specific CSS file only in the versions that need it.
Always prefer feature detection over browser (navigator.userAgent) detection.
The userAgent string is a poor indicator of whether a particular feature (or bug) is present. To compound the problem, much of the code that interprets userAgent does so incorrectly. For example, one browser-sniffing library expected the major version to be only a single digit, so it reported Firefox 15 as Firefox 1 and IE 10 as IE 1! It is more reliable to detect the feature or problem directly, and use that as the decision criteria for code branches. We recommend Modernizr as the easiest way to implement feature detection.
Run as little script as possible when the document is ready.
Techniques such as jQuery's $(document).ready() make it easy to run script as soon as the HTML on the page is loaded, which is usually the earliest possible moment it can be safely run. However, running a lot of complex script at this point can make the page appear sluggish and prevent the user from interacting with it immediately. Often the initialization for things like a tooltip or dialog can be delayed until the item actually needs to be displayed, with no noticeable stutter.
Start AJAX requests as early as possible if they're critical for user interaction with the page.
Since an AJAX request can take a long time, there's no need to wait for the HTML page to be ready before starting it. Instead, place the $(document).ready() call inside the AJAX completion function.
Delay-load non-essential scripts (e.g., Facebook Like, Google +1, Twitter).
Everyone wants their page to be popular on social networks, but social network scripts tend to be large and can cause sluggish response for the user. Waiting until the page has loaded before requesting these scripts can make the page responsive sooner. Even better, rethink whether these buttons are necessary at all, and whether they improve your page's overall experience.