How can you improve the performance of web pages?
Most developers look to JavaScript and image optimisation, server configuration, file minification and concatenation – even CSS tweaks.
Poor old HTML gets neglected, despite being the core language of the web.
HTML payloads are big and getting bigger. Pages on most top-100 sites require around 40k of HTML per page. Sites such as Amazon and Yahoo use thousands of lines of HTML per page. The youtube.com home page currently clocks in at a whopping 3.5K HTML elements.
Reducing HTML complexity and the number of elements in a page won’t improve parse time much – but well crafted HTML is a crucial foundation for building fast-loading pages and layouts that respond successfully to different viewport sizes.
In this article you’ll find out how to write clean, concise HTML that enables you to create content that loads fast and works well across a variety of devices. In the process, you’ll learn how to build sites and apps that are easier to debug and maintain.
There is always more than one way to write code – especially HTML. Rather than begin every sentence in this article with ‘In general …’ we’ve gone ahead and prescribed what in our experience usually works best. That doesn’t mean that every suggestion is right every time. |
tl;dr
- Separate concerns: use HTML to add structure, not to style content.
- Keep it clean: add code validation tools to your workflow.
- Keep it tidy: use tools and a style guide to maintain consistent code structure and formatting.
- Learn the language: get to grips with element structure and semantic markup.
- Ensure accessibility: use ARIA attributes as well as fallback attributes and content. Experience your site with a text only browser or screenreader.
- Test: try out your site on multiple devices and screen sizes, use emulators and performance tools.
HTML, CSS and JavaScript
HTML is a markup language for adding structure and meaning to content.
HTML should not be used to style content. Don’t put text in heading tags to ‘make it bigger’ or use blockquotes just for indentation. Instead, use CSS to change the appearance and layout of elements.
The default appearance of HTML elements is defined by a browser’s default stylesheet: Chrome, Firefox, Internet Explorer and Opera each have their own. For example, in Chrome the h1 element by default is rendered as 32px bold Times. |
Three general principles:
- Use HTML for structure, CSS for presentation and JavaScript for behaviour. CSS Zen Garden (12 years old this year!) shows this separation of concerns in action.
- Use HTML, then turn to CSS if and when required – and, if really necessary, move on to JavaScript. For example: in many cases it’s possible to use HTML for form validation and CSS or SVG for animation – without resorting to JavaScript.
- Put CSS and JavaScript in files separate from your HTML. This enables caching and makes code easier to debug. In production, CSS and JavaScript can then be minified, concatenated and inlined as part of your build process.
Document structure
- Use the HTML5 document type. Here’s a barebones document:
<!DOCTYPE html> <html> <head> <title>Recipes: pesto</title> </head> <body> <h1>Pesto</h1> <p>Pesto is good!</p> </body> </html>
- Link to CSS files at the top of the page, in the head element like this:
<head> <title>My pesto recipe</title> <link rel="stylesheet" href="/css/global.css"> <link rel="stylesheet" href="css/local.css"> </head>
That way, browsers have CSS information ready before parsing HTML.
- Put JavaScript at the bottom of the page, before the closing body tag. This speeds up page load – since the browser can render the page before parsing JavaScript – and has the helpful side effect of enabling JavaScript to refer to page elements:
<body> ...<script src="/js/global.js"></script>
<script src="js/local.js"></script>
</body>
- Alternatively, use the defer and async attributes – but be aware of their behaviour: script elements with an async attribute aren’t guaranteed to be executed in order. Also consider support: async is only implemented in Internet Explorer 10 and above; defer is only partially supported prior to Internet Explorer 10.
- Add handlers in JavaScript code. DON’T add them inline in HTML. For example, this is error prone and hard to maintain:
index.html:
<head> ... <script src="js/local.js"></script> </head> <body onload="init()"> ... <button onclick="handleFoo()">Foo</button> ... </body>This is much better:
index.html
<head> ... </head> <body> ... <button id="foo">Foo</button> ... <script src="js/local.js"></script> </body>js/local.js:
init(); var fooButton = document.querySelector('#foo'); fooButton.onclick = handleFoo();
Validation
A major reason for the success of the web is that browsers handle invalid HTML. There are even standardised rules for how browsers should render invalid code.
However, this is NOT a reason to be laissez-faire. Valid HTML is easier to debug and often smaller in file size, faster and less resource-hungry to parse and render. Invalid HTML can make successful responsive design difficult to implement.
Writing valid HTML is particular important when working with templates: what seems to work OK in an isolated chunk of code may go horribly wrong in combination with other content.
- Incorporate validation in your workflow: use validation plugins such as HTMLHint and SublimeLinter with your editor and incorporate validation in your build process using tools such as HTMLHint with Grunt. You can check code online with tools such as the W3C HTML validator, and share configuration files with project contributors.
- Use the HTML5 document type.
- Make sure to maintain HTML hierarchy: nest elements correctly, and make sure no elements are left unclosed. It can be helpful for debugging to add a comment to the closing tag of an element that holds a large amount of complex content – especially when using templating:
<div id="foobar">
...
</div>
<!-- foobar ends -->
- Make sure to add closing tags to all elements that aren’t self-closing.
For example, this will work:
<p>Pesto is good to eat... <p>...and pesto is easy to make.
But this is less likely to go wrong, since the end of each paragraph is made explicit:
<p>Pesto is good to eat...</p> <p>...and pesto is easy to make.</p>
- Closing tags are not required for list items and some very clever developers believe you should leave them out. Whatever you do, make sure to close every list element:
<ul> <li>Basil <li>Pine nuts <li>Garlic </ul>
- One place you absolutely must include closing tags is with the video and audio elements. These are not ‘self closing’:
<!-- wrong: liable to cause layout grief --> <video src="foo.webm" /> <!-- better --> <video src="foo.webm"> <p>Video element not supported.</p> </video>
- Link elements (used to include CSS) are self closing — it wouldn’t make sense for a link element to contain code — but a script element always requires a closing tag, even if it only links to an external JavaScript file:
<link rel="stylesheet" href="css/local.css"> ...<script src="/js/global.js"></script>
Conversely, cut clutter by removing unnecessary code:
- There is no need to add a slash to ‘self closing’ (void) elements such as <img> and <link>. (You can enjoy a comprehensive list of self-closing elements here.)
- Boolean attributes do not have a value: if the attribute is there, it’s true. In the following example, the video element will not autoplay and will not have controls (since this is the default):
<video src="foo.webm">
The following example doesn’t work as expected, because the presence of an attribute forces a true value:
<video src="foo.webm" autoplay="false" controls="false">
This works:
<video src="foo.webm" autoplay="true" controls="true">
This is more readable:
<video src="foo.webm" autoplay controls>
- Stylesheets and scripts don’t need a type attribute: CSS and JavaScript are the defaults.
- Omit the protocol when linking to external content: this prevents problems with mixed content. For example:
<a href="//en.wikipedia.org/wiki/Tag_soup">Tag soup</a>
Code formatting
Consistent formatting makes HTML code easier to understand, optimise and debug.
- Maintain an HTML style guide for project contributors (or use an existing one like Google’s).
- Use your editor to automate code beautification. For example, in Sublime Text add a shortcut for Reindent. You can check layout with code linters such as SublimeLinter or online tools such as CSS Beautify and JS Beautifier.
- Don’t go mad with hierarchical HTML indentation! This can easily get out of hand, so set rational defaults for when to begin an element at the left margin. Conversely, deep hierarchies may mean you need to refactor.
- Standardise on indentation and use either spaces or tabs, not both.
- Improve readability with sensible nesting. For example, it’s clear that this is a heading:
<h2><a href="/contact">Contact</a><h2>
…whereas at first glance this just looks like a link:
<a href="/contact"><h2>Contact</h1></a>
- Order elements in ways that won’t be a surprise to other developers – or you in six months. For example, a footer element should go at the bottom of your .html page, even though (in theory) it could be put anywhere.
- Standardise on single or double quotes.
- Use lowercase code for tag and attribute names. UPPERCASE IS TIRESOME:
<A HREF="/">Home</A>
Mixed case is EVEN worse:
<H2>Pesto</h2>
Semantic markup
Semantic means ‘relating to meaning’.
HTML markup adds meaning to content: element and attribute names describe the role of content.
HTML5 introduced a number of new ‘semantic elements’ such as <header>, <footer> and <nav>.
Using the right element for the right content is good for accessibility and helps ensure your code is understandable:
- Use <h1> (<h2>, <h3>…) for headings, <ul> or <ol> for lists.
- Note that <article> headings should begin with an <h1> (reasoning here).
- Where appropriate, use the HTML5 semantic elements such as <header>, <footer>, <nav> and <aside>.
- Use <p> for body text, HTML5 semantic elements (or as a fallback) to structure content – not vice versa.
- Use <em> and <strong> rather than <i> and <b>: use HTML to add meaning, not styling hints.
- With forms use the element, input types, placeholder text, and the required attribute to enforce validation. (Pete LePage’s articles on Web Fundamental show how.)
- Mixing text and elements, as children of another element, tends to lead to layout bugs. For example, this:
<div>Name: <input type="text" id="name"></div>
is better expressed as this:
<div> <label for="name">Name:</label> <input type="text" id="name"> </div>
This has a bonus side effect: the for attribute means that clicking on the label automatically throws focus onto the input element. This is particularly useful for checkboxes and radio buttons.
Layout
To reiterate: HTML should be used to add meaning and structure to content, not for styling.
- Use <p> elements for text, not for layout. By default, <p> has margins and other styles applied by the built-in browser stylesheet.
- Avoid using <br> for line breaks: use block elements or the CSS display property instead. The <br> should only ever be seen within text – and even then, only very rarely (for example, as a form of punctuation within poetry).
- Avoid using the humble <hr> to add horizontal lines: CSS border-bottom is a better option. It may make sense to use <hr> to denote a thematic break.
- Don’t use divs unnecessarily: the W3C HTML spec describes the div as ‘an element of last resort, for when no other element is suitable’ (sixrevisions.com). Style inline elements such as links and img elements with display: block, rather than putting them in divs or (worse) using <br>.
- Learn which elements are block level, to avoid unnecessarily putting block level elements inside divs. For example, there is no need to put a list inside a div element.
- Do not use tables for layout. Do use tables for tabular data.
- Flex box is now widely implemented: use it.
- Use CSS padding for padding and margin for margins: understand the box model.
- Standardise on margins: it’s often best to add margins to the bottom and the right of elements, rather than the top or left. Whatever you do, avoid mixing top and bottom or left and right. Use the last-of-type selector to avoid redundant margins.
HTML emails
Coding for email is nothing like coding for the modern web.
We won’t go into the gruesome details here – suffice to say that best practice for HTML email coding resembles web development from the late 1990s: tables, shim GIFs, inconsistent and minimal CSS, patchy image and media support, little or no support for JavaScript, and lots of minor hacks. (For an unpleasant taster, take a look at MailChimp’s most basic template.)
More information here, here and here.
CSS
This article is about HTML, but here are some basic CSS tips:
- Avoid inline CSS. For performance optimisation, CSS files can be inlined as part of your build process.
- Use an ID once and once only: there should only be a single element with id=”foo”, id=”bar”, or any other ID.
- Use classes when you want to refer to multiple elements – and take a look at BEM syntax. Where possible, use class attributes on a parent rather than children. For example:
<!-- verbose :( --> <ul> <li class="ingredient">Basil</li> <li class="ingredient">Pine nuts</li> <li class="ingredient">Garlic</li> </ul>
<!-- better :) --> <ul class="ingredients"> <li>Basil</li> <li>Pine nuts</li> <li>Garlic</li> </ul>
Accessibility
Consider the range of devices, contexts and input methods that will be used to interact with your HTML:
- Use semantic elements.
- Provide fallbacks: add captions and subtitles with the track element, include fallback text and/or images in video and audio elements; use poster images for video; add alt attributes to every image (give the alt attribute an empty value if the image is only for decoration).
- Add title attributes to links – but only if the title adds meaning and doesn’t just repeat the link text.
- Use type and placeholder elements in input elements.
- Use ARIA attributes.
Random bonus extra suggestions
- Make sure to encode characters with special meaning in HTML, such as < and &. For example: <title>HTML & JavaScript</title>.
- Conversely, do not unnecessarily encode characters such as en dashes (for example, 4–5 weeks) or currency symbols such as ¢ and €.
- Add comments if and only if it’s not obvious what’s happening in code. (And bear in mind that code commenting often shows a need for refactoring – good HTML, even when it’s complex, is generally self-explanatory.)
- Where it makes sense – with all-capitalised headings, for example – apply text case via the CSS text-transform and font-variant properties, not by entering uppercase text. You may change your mind later! Also, if users copy text, they probably want upper and lower case. The following <h4> is displayed in small caps, but when copied will be upper and lower case.HTML:
<h4>W3C Web Accessibility Initiative ARIA guidance</h4>
CSS:
h4 { font-variant: small-caps; }
And finally…
Whatever you do, test!
Build HTML testing into your workflow, toolchain and deployment processes.
Test page load on a variety of devices, on large and small screens, in a variety of connectivity contexts. Try interacting with your page with a text-only browser such as Lynx, or with a screenreader such as ChromeVox. Use emulators such as Chrome Dev Tools device mode to monitor changes. Page Speed, Web Page Test and other tools can be integrated with your workflow to automate testing before or after deployment.
For more information about how to write high performance HTML, follow the links in HTML, CSS and JavaScript resources and check out the guidance on Web Fundamentals.
Reblogged this on keithmiketom and commented:
Great read
Thank you, Sam, for the post! It’s been good and !important to revise the html basics in one place.
Finally!!! Nobody ever talks about HTML cleanlyness and performance. I insist on this topic whereas I keep seeing HTML bloated with tons of classes (thanks to bootstrap!). I shared this on Twitter. I hope you get lots of page views!
Pingback: High performance HTML | Social Media for Business Killester CFE
Just keeping the html as minimal as possible and easier to reason about helps so much, not just for performance but development and maintenance down the line.
Also keep the nesting depth as low as possible for best mobile performance.
Take better advantage of html and body tags themselves as part of your styling for your website.
Hell you could even set display: block on your head elements and children.
Wow great post! Your screenshot of the code highlights formatting perfectly. Keeping your code clean is nice especially when working with a group of people. It’s not a bad idea to have a coding style guide when working with groups. It makes things much easier when some one else looks at your code and makes future updates a whole lot easier. Cheers
Your logic surrounding header hierarchy is flawed. You shouldn’t have an H2 in an article that isn’t preceded by an H1. http://webdesign.tutsplus.com/articles/the-truth-about-multiple-h1-tags-in-the-html5-era–webdesign-16824
Good spot – thanks. Fixed.
Hi Sam,
This post is just what I would like to have written. During my day job, I see a lot of horrifying HTML. It is obvious that not everyone cares deeply about it.
The Google HTML/CSS Style Guide, you link to, does have a section that makes my gut revolt:
http://google-styleguide.googlecode.com/svn/trunk/htmlcssguide.xml?showone=Optional_Tags#Optional_Tags
Thanks for a very nice post.
Thanks Bo – appreciated.
Have to say, my gut revolts at that Google Style Guide suggestion too :)! Less verbose and maybe more elegant, but…
I’m not really a fan of removing either.
Great article, thanks!
Suspected typo: … should read …
Thanks – look forward to more information about the typo ;).
Great article, thanks!
Suspected typo: label… label
should read
label … /label
Thanks! Fixed.
Pingback: My readings in 2015 week 15 | My path to become awesome dev
> Avoid . It doesn’t add meaning or structure to content
The hr element does add meaning and structure to a page (http://html5doctor.com/small-hr-element/). Agreed, it’s not a good choice for visual styling, but it does have semantic value, even if hidden with CSS.
Thanks for pointing this out. I’ve updated the text.
Pingback: 如何写出高效率的HTML — 好JSER
Pingback: 1p – A Primer on High Performance HTML | Profit Goals
Pingback: 1p – A Primer on High Performance HTML | Exploding Ads
Mucha gente debería aprender de éste documento. Imprescindible. Essential.
Gracias!
Mixing text and elements, as children of another element, tends to lead to layout bugs. For example, this:
Name:
is better expressed as this:
Name:
I’d propose rather
Name:
When clicking on the label, it automatically throws the focus onto the input element that is nested. This is particularly useful for checkboxes and radio buttons.
Good call – added some text. Thanks!
Pingback: Colmandesign LLC
Pingback: Free Hand-Picked Resources for Designers and Developers – May Edition | Designer News
Pingback: Free Hand-Picked Resources for Designers and Developers – May Edition - Jarel Culley
Pingback: Tangan-Dipetik Sumber Daya Gratis untuk Desainer dan Pengembang – Mei Edition | CiptaPedia | CiptaPedia
Pingback: 编写高性能HTML网页应用 - HTML - 微笔记[SparkChang.com]
Pingback: High performance HTML | The Art and Design of KeithMikeTom
This is one of those posts every web developer should read, bookmark and reread now and then.
Congratulations.
Thank you!
best information i have ever since am new in bloging thanks a lot
Pingback: HTML5 Weekly No.183 | ENUE
Pingback: 编写高性能HTML网页应用 | 太空兔-形动力
Pingback: HTML5 WEEKLY NO.221 – ENUE
> nest elements correctly, and make sure no elements are left unclosed
I have always gone by this rule, but I notice you never close the script tags. I’d be interested to know about the validity of this, because I can’t work out from w3.org whether it is considered “valid” HTML.
Thanks, Greg.
Thanks!
I meant to fix this and forgot.
I’ve added tags as well as a note.
Sam — great name by the way (:
Thanks for the great, thorough post. I do have one thing to add..
>Where it makes sense – with all-capitalised headings, for example – apply text case via the CSS text-transform and font-variant properties, not by entering uppercase text. You may change your mind later! Also, if users copy text, they probably want upper and lower case. The following is displayed in small caps…
The catch with CSS text-transform, is with i18n and the different capitalizations Turkish used to be an issue, but that seems to be fixed according to MDN, but Gaelic is an issue — read more here: https://developer.mozilla.org/en-US/docs/Web/CSS/text-transform
Not trying to dispute what you said, but letting people know there are some gaps in the tech as it stands Jan 2016.
Thanks again,
Samuel
Pingback: My readings in 2016 week 5 | My path to become awesome dev
Having CSS in a separate file will measurably degrade page performance compared to putting the CSS in the head of the HTML file. There is no good reason to force users to download more files. Caching isn’t going to make up for the benefit of having the CSS right there in the head in most cases (test it). This is why Google’s AMP spec requires all CSS to be in the head, and bans external stylesheets.
Pingback: How to Design Content that can Work Well on different Devices & Viewports
Pingback: How to Design Content that can Work Well on different Devices & Viewports – Tech News
Pingback: 如何写出高效率的HTML-小伍大叔
Pingback: 如何写出高效率的HTML | 小伍大叔 - 推荐好软件