Front End Development Best Practice

Over the past ten years the discipline of Front End Development has emerged from the implementation of web standards by web developers. However, web standards are really only the beginnings of a well developed website. The standards we adopt beyond web standards also inform the quality of our development, and as a result it is often useful to develop best practices for the future. This document is intended as the starting point for your own best practices.

An exceptional website is:

Make content available to all

A high quality website should be available to as many users as possible, regardless of their client, technology, abilities, and situation. The web is entirely designed for sharing content; there’s really no reason to break that paradigm.

Be client agnostic

The users' choice of browsing client should be mostly irrelevant to your decisions as a web developer. Obviously there are occasional exceptions to this rule, usually where user input and control is different. Examples of exceptions are building in accessibility or developing mobile, touch, and printer stylesheets. Even with these exceptions, however, we should always attempt to support the largest range of devices with the minimum adjustment.

A browser support standard will help

It’s important to understand the technologies being used to browse your content and test accordingly. This information is best gleaned from your web analytics and log files. Once you have analysed and assimilated that information, you’ll likely want to follow a graded browser support strategy to allow you to focus your development and testing efforts on the browsers that matter the most to you whilst not being held back by legacy browsers being used by a small-but-significant proportion of your visitors. In lieu of having your own browser stats, here’s what we’d recommend based on global browser market share:

Primarily, test and ensure that the visitor gets a first-class browsing and interaction experience in the following browsers:

This doesn’t mean the pages have to be pixel-perfect between browsers, but as close as reasonably possible to the design requirement with all of the functionality and interactivity working perfectly.

Secondly, test and ensure that the visitor gets a useable and not-obviously-broken experience with:

Once you have a better understanding of the users of your site, you may opt to drop support for any of these combinations, or even add some additional browsers.

Other browser support matrices are available:

Support browsers effectively and avoid pixel perfection

Requiring the same experience for all users creates a barrier to participation. Availability and accessibility of content should be our key priority. With this as an ideal, there is absolutely no reason to seek pixel perfection cross-browser. A good front end developer should take responsibility to communicate this and educate where appropriate.

See also:

Improve graceful CSS degradation with unobtrusive hacks

Sometimes there is a need to overcome significant rendering issues with certain browser engines. To do this you should not be afraid of using an unobtrusive hack such as the IE underscore and star hacks:

Where Internet Explorer 6 and 7 need extra style rules to create a non-broken browsing experience, use the underscore and/or star hacks to create IE-specific style rules. Use CSS comments to document the use of CSS hacks.

.module {
    float: left;
    _display: inline; /* Double margin fix for IE6 */
}

The benefit of managing the adjustment this way is that you can seat it next to the original property adjustment and comment accordingly. This allows a clearer understanding when you come back to this rule later during maintenance.

This rule only applies where the CSS hack is unobtrusive enough to not require an entirely new selector, i.e. it only affects a single property declaration as above.

See also:

Target IE specifically with conditional comments

If we’re entirely honest with ourselves as web developers, we can probably admit that the majority of woes we experience in CSS are as a direct result of features that any given version of Internet Explorer has not implemented.

The best technique for targetting IE is to adopt conditional comments to add IE specific classes to the body element of the page in HTML:

<!--[if lt IE 7 ]>
<body class="ie ie6">
<![endif]--> 
<!--[if IE 7 ]>
<body class="ie ie7">
<![endif]--> 
<!--[if IE 8 ]>
<body class="ie ie8">
<![endif]--> 
<!--[if IE 9 ]>
<body class="ie ie9">
<![endif]--> 
<!--[if gt IE 9]>
<body class="ie">
<![endif]-->
<!--[if !IE]><!-->
<body>
<!--<![endif]-->

We’re using the body element here because attaching the ie classes to the html element would result in IE not forcing standards mode and potentially trigger various ‘quirks’ rendering modes.

With the classes in place we can then write CSS rules like so:

#navigation {
    float: left;
    margin-right: 30px;
    background: #ccc;
    background: #ccc -webkit-gradient(linear, left top, left bottom, from(#999), to(#ccc)) no-repeat;
    background: #ccc -moz-linear-gradient(top,  #999,  #ccc) no-repeat;
}
/*
    IE specific rule to fix double margin bug, remove potentially 
    render-slowing filters and trigger hasLayout.
*/
.ie #navigation {
    display: inline;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff999999, endColorstr=#ffcccccc);
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff999999, endColorstr=#ffcccccc)";
    zoom: 1;
}

This stops us having to maintain a separate stylesheet specifically for IE and keeps related CSS rules together. This improves readability and ensures that all browser fixes are next to each other when you return at a later date.

See also:

Favour object detection over browser sniffing in JavaScript

Browser “sniffing” is the act of attempting to discern a user’s browser by some means. This information can then be used in conditional code to control output or interaction for a specific browser. This technique used to be used a great deal in JavaScript, when browsers had very different rendering engines and didn’t all conform to web standards.

Browser sniffing is not a good solution. If you adopt it, you will usually end up maintaining separate streams of development whenever you need to add or update features. It’s also of note that some browsers identify themselves incorrectly to get around legacy browser sniffing code. In short, this technique does not scale.

A more scalable approach is to use object detection in JavaScript to discover if a feature is available before you use it. An example of this might be:

// Test if querySelector exists on the document object
if (document.querySelector) {
    // Make use of it
    element = document.querySelector(selectors);
}

The obvious advantage here is that it is browser agnostic; there are no assumptions as to which browser is involved at all. Using this technique, we overcome the scalability and maintenance headaches incurred through browser sniffing, and we can develop for graceful degradation from the outset.

See also:

Leverage web standards

Implementing web standards is the foundation for maintainability, accessibility (both for machines and people), stability, and scalability. Web standards are defined by the people who use them; browser makers, web developers, content providers, and other organisations.

Creating web pages in accordance with the standards shortens site development time and makes pages easier to maintain. Debugging and troubleshooting become easier, because the code follows a standard.

Use HTML not XHTML

Always use HTML served as text/html instead of XHTML. There are many reasons to avoid serving XHTML as XML:

Some authors opt to write markup that will function when served as XML or text/html. Given the subtle differences between the handling of XML and text/html, this is a great way to double the testing you need to do for no significant benefit.

Whilst the well-formed nature of XHTML is certainly attractive to the markup purist, you could use a linter to enforce your chosen style of (potentially XMLish) HTML.

See also:

Use the HTML Living Standard DOCTYPE

In the brave new world of HTML as a living standard, the ideal DOCTYPE to use is as follows:

<!DOCTYPE html>

The first character of the DOCTYPE string should be the very first character in your document (even whitespace counts). Other DOCTYPE strings may cause browsers to render your document using various ‘quirk’ modes intended for supporting legacy documents.

Always specify a language and text direction

For improved internationalisation support, always specify a language and direction in the root element of your document. This is used by all user agents, screen readers, and search engine spiders to better understand how to voice your content:

<html lang="en-gb" dir="ltr">

See also:

Always specify a character-set within your document

It is important to make sure you specify a character-set within your document in case so that you can be sure your content is displayed properly by user agents. The correct position for this is before any other content or meta content within your page. For this reason, it’s best to place it as close to the opening head tag as possible:

<!DOCTYPE html>
<html lang="en-gb" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title>Chronological displacement engines of the Renaissance</title>

See also:

Use structured semantic markup

Decorate your content with well structured semantic markup. This means making use of HTML elements to provide more information about the content within them.

Semantic markup includes semantic element identifiers

Build in accessibility from the start

Accessibility is not a “feature”, it is a requirement. Ostracising or punishing a user simply because of the technology they use to browse your content is unacceptable, and even illegal in some countries. Further to this, developing accessible websites is not difficult but does require a little extra thought and diligence at times.

Use links and input buttons for clickable elements

Links and buttons are focusable and activatable by default in a browser, which means they are perceivable and operable to screen reader users and keyboard-only users. Rich JavaScript widgets (like video player controls) should be progressively enhanced on-top of elements that, by default, are already accessible.

Presentation

Use CSS3 presentation effects over old presentation hacks

The following effects can be done in CSS3, with no fallback when not supported in browsers (like Internet Explorer 6 & 7):

Don’t forget to add vendor-specific style properties where necessary.

Use overflow to self-clear contained floats

Where possible use overflow on the containing element to clear floated elements.

.module {
    overflow: hidden;
}

Where content inside of this container needs to spill out of the containing div (for example a dropdown menu near the bottom of the container), then use the content after method of clearing (using zoom: 1 for Internet Explorer):

.editing-panel {
    overflow: visible /* To unset a previous style-rule */
    _zoom: 1; /* self-clearing for IE */
}
.editing-panel:after {
    content: '.';
    display: block;
    clear: both;
    height: 0;
    visibility: hidden;
}

Dynamic Behaviour

Add an enhanced class to modules or widgets

Where possible.

Otherwise…

Add a js class when JavaScript is available

jQuery

Use element names when selecting by class names

In jQuery lookups for elements with a specific class where possible use an element name as part of the selector. So instead of :

var $todoItems = $('.todo');

Specify an element name in the selector:

var $todoItems = $('li.todo');

Under the bonnet browsers that don’t have a native document.getElementsByClassName() method, will use a document.getElementsByTagName() method with the element name instead of a *. This will return a smaller Node list and thus be quicker.

Use references instead of repetitive DOM lookups

If you are doing some non-trivial work with a particular jQuery wrapped set, store the wrapped set as a reference so that you can avoid recalculating the same wrapped set over and over. This approach also allows breaking down the code into easier to digest and understandable chunks.

Also to make it more obvious that we have a reference to a jQuery wrapped set prefix the reference variable with a $.

var $items = $('ul.checklist li');

if ($items.length) {
    $('span.total').text($items.length);
    $items.filter('.done').remove();
}

Break chained methods over multiple lines for readability

Although chaining is a useful feature of jQuery, it’s too easy to create unreadable code. So be generous on the use of spacing and linebreaks and aim to make the code as readable as possible.

   $('form.signup')
        .bind('submit', formSubmitHandler)
        .bind('validate.form', validateFormHandler)
        .bind('save.form', saveFormHandler)
        .removeClass('disableSubmit');

Using jQuery’s end() to unwind the chaining stack is a sure sign that your chaining statement is in danger of becoming difficult to understand. When your chaining gets this complex, it’s best to get a reference to the common ancestor and logically separate your complex chain into several less-complex chains.

Usability

Always put a useful URL in href

Browsers allow users to do various things to hyperlinks. Most obviously, you can open the link in the current browsing context (tab or window). But other secondary functions are often provided, including opening the link in another browsing context, bookmarking the link, and copying the link elsewhere. Browser addons add yet more functionality, such as opening the link in a different browser or sharing it via social bookmarking sites. Sometimes these secondary functions are attached to pressing a key and mouse button in combination, sometimes they are listed in the context menu.

The common practice of using a href="#" as a hook for some JS to transition to a new state (e.g. open a dialog for adding an item) breaks all these secondary functions, since the link in the href does not represent the expected state (instead it points to the top of the current document).

Instead, use a URL that can serve up a representation of the expected state (e.g. http://example.com/add), or use a button if there’s no such URL.

Performance

Profile first, optimise second

Performance is just one aspect that a developer has to concern themselves with when creating a modern website or web application, albeit one that has a definite impact on user satisfaction. Blindly following every piece of optimisation advice you find on the Internet would be time consuming, lead to harder-to-understand code and would predominantly be a colossal waste of time.

Profile your code as you’re writing it, and profile the entire project at regular intervals, to identify the hot spots that are causing you the most pain. Once you’ve done that, you can then apply just the performance optimisations that will help you overcome those issues. Remember to test these as you’re going though — advice on the Internet has an unnerving habit of being misleading, and the techniques on offer may not exactly apply to your situation.