Using HTML5 semantic elements today
Over the past year, the argument over whether or not to use the new HTML5 semantic elements has morphed into how to use the new HTML5 semantic elements. All major browsers officially supporting these elements before the end of the year (many before the end of the quarter), and as such, the time to start using these new elements is now. Of course, the world is not just made up of HTML5-capable browser and so the question of writing for backwards compatibility is a major question that many have attempted to answer.
The problem
The biggest issue with using the new semantic elements is how non-supporting browsers deal with them. There are essentially three possible outcomes when HTML5 elements are used in a page:
- The tag is considered an error and is completely ignored. The DOM is constructed as if the tag doesn’t exist.
- The tag is considered an error and a DOM node is created as a placeholder. The DOM is constructed as indicated by the code but the tag has no styles applied (considered an inline element).
- The tag is recognized as an HTML5 tag and a DOM node is created to represent it. The DOM is constructed as indicated by the code and the tag has appropriate styling applied (in many cases, as a block element).
As a concrete example, consider this code:
<div class="outer">
<section>
<h1>title</h1>
<p>text</p>
</section>
</div>
Many browsers (such as Firefox 3.6 and Safari 4) will parse this as a top-level <div> element with an unknown child element (<section>) that is created in the DOM but treated as an inline element. The <h1> and <p> elements are children of <section>. Because <section> is represented in the DOM, it is possible to style the element. This is case #2.
Internet Explorer prior to 9 parses this as a top-level <div> but sees <section> as an error. So <section> is ignored and then <h1> and <p> are parsed, both becoming children of <div>. The closing </section> is also seen as an error and skipped. The effective understanding of this code in the browser is equivalent to:
<div class="outer">
<h1>title</h1>
<p>text</p>
</div>
So older Internet Explorer browsers actually recover quite nicely from unknown elements but creates a different DOM structure than other browsers. Because there is no DOM representation of the unknown element, you also cannot apply styles to <section>. This is case #1.
Of course, HTML5-capable browsers such as Internet Explorer 9, Firefox 4, and Safari 5 create the correct DOM structure and also apply the correct default styles to that element as specified in HTML5.
So the big problem is that browser produce not only different DOM structures for the same code, but also different styling rules for the same DOM structures.
The solutions
A number of people have come up with a number of different solutions to using HTML5 elements in pages today. Each attempts to attack one or more of the specific problems already mentioned in an effort to provide cross-browser compatibility.
JavaScript shims
JavaScript shims aim to primarily solve the problem of styling HTML5 elements in older Internet Explorer browsers. There is a now-well-known quirk in Internet Explorer where it won’t recognize unknown elements unless one of these elements has already been created via document.createElement(). So the browser will create a DOM element and will allow styling of a <section> element so long as document.createElement("section") is called.
Shims such as html5shim[1] use this capability to ensure that HTML5 elements correctly create DOM elements in Internet Explorer and therefore allow you to apply styles. Shims typically also set HTML5 block element to display: block so they display correctly across other browsers as well.
I don’t like this approach because it breaks one of my primary web application principles: JavaScript should not be relied on for layout. This is about more than creating a bad experience for those with JavaScript disabled, it’s about making a predictable and maintainable web application codebase where there is a clear separation of concerns amongst layers. It does have the benefit of producing the same DOM structure across all browsers, thus making sure your JavaScript and CSS works exactly the same everywhere, but that benefit doesn’t outweigh the downside in my opinion.
Namespace hack
Never short on hacks, Internet Explorer also has another technique for making the browser recognize unknown elements. This one was first gained wide attention through Elco Klingen’s article, HTML5 elements in Internet Explorer without JavaScript[2]. This technique involves declaring an XML-style namespace and then using elements with the namespace prefix, such as:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:html5="http://www.w3.org/html5/">
<body>
<html5:section>
<!-- content -->
</html5:section>
</body>
</html>
The html5 prefix is just purely pretend and isn’t official at all – you could just as well have the prefix be “foo” and the effect would be the same. With the prefix in place, Internet Explorer will recognize the new elements so that you can apply styles. This also works in other browsers, so you’ll end up with the same DOM and same styling everywhere.
The downside is clear: you must use XML-style namespaces in an HTML document and also use them in CSS, meaning something like this:
html5\:section {
display: block;
}
This isn’t the way I’d like web developers to have to write their code. It’s a brilliant solution to the problem but one that teaches what I consider to be an unnatural application of the new elements. I don’t want to see files full of namespaced elements.
“Bulletproof” technique
I was first exposed to this technique at YUIConf 2010, when Tantek Çelik gave a talk entitled, HTML5: Right Here, Right Now[3]. In that talk, Tantek suggests using an inner <div> element for each of the new HTML5 block elements, and to include a CSS class name on that <div> indicating that it represents the HTML5 element. For example:
<section><div class="section">
<!-- content -->
</div></section>
The intent of this approach is to ensure that content flows correctly in all browsers. Using one block element inside of an HTML5 element that should be a block means you’ll either have a single block element (Internet Explorer < 9), a block element inside of an inline element (Firefox 3.6, Safari 4, etc.), or a block element inside of a block element (Internet Explorer 9, Firefox 4, Safari 5, etc.). In each of these three cases, the default rendering is the same.
Tantek did note one exception where this doesn’t work, and that is with <hgroup>, which explicitly disallows non-heading child elements. For that he recommended putting the <div> on the outside:
<div class="hgroup"><hgroup>
<!-- content -->
</hgroup></div>
For styling, Tantek recommended not to try to style the HTML5 element itself but rather to style the surrogate <div>. So instead of this:
section {
color: blue;
}
Use this:
.section {
color: blue;
}
The rationale is that it will be easy to automatically convert this pattern into one referencing the HTML5 element tag name later on. I’m not a fan of this part of his suggestion, since I generally do not like applying styles via tag name.
The downside of this approach is that different browsers create different DOM structures and so you must be careful in how you write JavaScript and CSS. For instance, using the immediate child selector (>) across an HTML5 element won’t work in all browsers. Also, directly accessing parentNode might result in a different node in different browsers. This is especially obvious in code such as:
<div class="outer">
<section><div class="section main">
<!-- content -->
</div></section>
</div>
If you then have a selector such as section > .main, it will not be applied in Internet Explorer 8 and earlier. Whenever you cross the HTML 4 to HTML5 to HTML 4 barrier, you’ll end up with these issues.
Reverse bulletproof technique
There are other posts, such as Thierry Koblentz’s, HTML elements and surrogate DIVs[4] that have explored reversing Tantek’s approach so that the HTML5 elements appear inside of the <div> elements. For example:
<div class="section"><section>
<!-- content -->
</section><div>
The only difference is the placement of the HTML5 element – everything else is the same. Proponents like this technique because of its consistency (works the same for all elements, including <hgroup>). It’s worth noting that this approach has the same caveats as Tantek’s as part as selector usage and JavaScript DOM traversal goes. It’s main advantage is the consistency of technique.
My approach
My main goal in choosing an approach was to ensure that I would only have to make changes to the HTML of a page. That meant zero changes to either CSS or JavaScript. Why make such a requirement? The more layers of a web application (or any application) that have to change, the more likely you are to introduce bugs. Limiting the changes to one layer limits the introduction of bugs and, if they occur, limits your search for the underlying problem to one area. For example, if a layout breaks, I’ll know it was because I added <section> rather than the combination of that plus a change to the CSS that styles that area.
After researching each of these techniques, doing some prototyping and testing, I eventually arrived back at Tantek’s approach. It was the only one where I could get all of the existing pages I was prototyping with to work without requiring changes to CSS and JavaScript. Now, I didn’t follow his approach to the letter and made several changes where I thought improvements could be made.
First, I never styled anything based on the class name representing the HTML5 element (so no .sectionin my selectors). I kept the same <div> elements that were already in the page and used the semantic class names that were applied to these elements as my style and JavaScript hooks. For instance, this code:
<div class="content">
<!-- content -->
</div>
Became this code:
<section><div class="section content">
<!-- content -->
</div></section>
With this change, I still used .content as the style and scripting hook for that area of the page. In doing so, the JavaScript and CSS I already had didn’t need to change.
Second, instead of having a special case for <hgroup>, I opted not to use it. The honest truth is that I didn’t find anywhere in any of my existing pages where this element would have been useful. Since <hgroup> can only contain headings, it’s mostly safe to include <hgroup> on its own if you really want to (assuming its contained within another block element).
I did spend a considerable amount of time bouncing back and forth between bulletproof and reverse bulletproof trying to determine which one worked best. The key determining factor for me was that reverse bulletproof required me to add CSS to make it work. In browsers that created a DOM node for the HTML5 element but did not apply default styling, having an HTML5 block element inside of a <div> messed up my layouts on more than one occasion because they became inline elements in older browsers. I had to explicitly add rules to make them into block elements to make my layouts work, and that broke my own requirement of not changing CSS to make things work.
The proof
One of the things I’ve found incredibly frustrating in this realm of discussion is how people too quickly dismiss one approach because they can find at least one situation where it doesn’t work. None of the solutions I presented here is perfect; none of them work in every single situation you may run into. If you give me any technique I can virtually guarantee you that someone can come up with a situation where it won’t work. That doesn’t invalidate the technique, it simply informs you of the technique’s limitations so you can make a better decision.
In my research I took several existing pages and converted them to use the modified bulletproof technique. I put them in pages with simple layouts and complex layouts, pages with and without JavaScript interactions. In each case, the only changes I made were to the HTML and everything continued to work correctly (no changes to JavaScript or CSS). What about those caveats about child nodes and parent node relationships? The interesting thing is that I never ran into these problems.
Granted, the reason it may have been so easy for me is because of the rigor I apply to my coding. I religiously double-check that:
- Tag names and IDs are not being used to apply styles (only use class names)
- CSS selectors are as general as possible and use as few selector types as possible
- JavaScript doesn’t rely on a specific DOM structure to work
- Tag names aren’t being used to manipulate the DOM
Another interesting thing I noted is that I was using the HTML5 elements as containers. These new elements really are just boundaries between groups of functionality rather than anything else. You spend most of your time styling and scripting items inside of these boundaries rather than crossing the boundaries themselves. Since my JavaScript and CSS targets what’s going on inside of containers, everything continued to work. I suspect this would be the case for most sites that have been well-coded.
Conclusion
The technique I ultimately decided on and would recommend to others is a modification of Tantek’s bulletproof technique. Clearly the name is a bit of a misnomer as there are some side effects in CSS and JavaScript, but in my experiments it really did seem to be the one approach that allowed me to change just the HTML of a page and have everything continue to work. I’m sure the debate will continue both inside of companies and on the Internet in general, and I hope this post helps you make an informed decision.
References
- html5shim
- HTML5 elements in Internet Explorer without JavaScript, by Elco Klingen
- HTML5: Right Here, Right Now, by Tantek Çelik (Video, Slides)
- HTML elements and surrogate DIVs, by Thierry Koblentz
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
Both comments and pings are currently closed.




23 Comments
What benefit do these techniques provide over using HTML 4.01? The JavaScript shims technique allows developers to use clean HTML5 markup at the expense of relying on JavaScript for layout. The other techniques create messier markup and the bulletproof and reverse bulletproof techniques add more nodes to the DOM.
Unless a developer needs the functionality of a particular element, e.g. the video element, why bother doing this to your code?
Mike Pfirrmann on March 22nd, 2011 at 9:51 am
HTML5 tags are meant for simple, elegant and semantic markup. These “bulletproof” techniques completely defeat the purpose. Why not just use the div?
A Random Commenter on March 22nd, 2011 at 9:57 am
what concrete benefit do we gain from over engineering our code by trying to support HTML5 and old style browsers?
I’m not criticising anyone for wanting to investigate these issues but I don’t really see what useful purpose doubling my markup just to shoehorn HTML5 elements into the page serves. I appreciate other people taking the time to think about it in real world solutions but what’s the need we are fulfilling here?
I wouldn’t put a javascript shim into a public site because of the need to support a non-js version in IE6 although I think thats a great solution.
Changing your actual DOM structure even in as light a way as possible is only going to increase your development time and force you to fork your JS with browser detection.
I guess what I’m asking is, is it really worth it and why?
mairead on March 22nd, 2011 at 10:26 am
First off, thanks for this thorough write up. Nice to see all these techniques in one place.
What’s the benefit to using HTML5 markup now? Will this result in better SEO? Accessibility? Is it worth it?
I’m loath to add, extra html markup for any reason in general. Especially now I feel like I’m finally turning the page on creating extra markup for things like rounded corners and drop shadows.
For now, I’m thinking it might be better to just create markup like in preparation for the day that it’s safer to switch to . Or maybe that’s the point? “That day” is still many years away?
mundi on March 22nd, 2011 at 11:16 am
oops, my tags got eaten. i meant to say…
For now, I’m thinking it might be better to just create markup like div — class=”section” — in preparation for the day that it’s safer to switch to — section –. Or maybe that’s the point? “That day” is still many years away?
mundi on March 22nd, 2011 at 11:21 am
Everyone – the question of concrete benefits is completely valid. The truth is that at this very moment, there are only small benefits, which are mainly learning to use the new elements and learning to think in terms of their usage. The real benefits come down the road once the world has adjusted. That’s when you’ll see improved accessibility and probably some really cool tools we can’t even dream of right now. At some point, you will want to start using these elements, the question is “if”, it’s “when”. I want to start using them now so that I get the benefit later. I generally prefer to be ahead of the curve rather than behind it. For others, it may not matter so much.
Nicholas C. Zakas on March 22nd, 2011 at 11:39 am
thank you for the great write up Nicholas, but I’ll continue to use the #html5 elements and use a shim.js to apply styling to them. In my opinion the web needn’t be held back by a small minority. And the occasional breaking of a site in (for instance) ie6 without javascript will for the sysadmin to upgrade the old xp machines in one of these big corporations where ie6 still lives.
In the real world the usage of legacy browsers is continuing to slink and ‘forcing’ it is no bad thing I say.
But, I need to say that you should code so that the minority still has a usable experience without JavaScript. This does not mean the same experience…
After al HTML is a very accessible medium and it’s content shouldn’t be dependent on javascript.
Wilfred Nas on March 22nd, 2011 at 12:04 pm
Nicholas, thanks for this great overview.
Let me add one more thing concerning HTML5 Tags in IE<9:
Even with those document.createElement shims it's impossible to dynamically insert html containing such tags via innerHTML. I'm sure you'll probably (sooner or later) will stumble upon this problem.
Luckily, there's also a solution for that:
See http://jdbartlett.github.com/innershiv/
Christopher Blum on March 22nd, 2011 at 6:25 pm
Have you checked out HTML5-Broiler-Plate ?
http://paulirish.com/2011/html5-boilerplate-went-1-0/
Jesse House on March 22nd, 2011 at 8:36 pm
[...] to cram support for HTML5 elements into your CSS offends you, Tantek Çelik and Nicholas Zakas have a solution for you: nest a block-level element inside your semantic HTML5 element and style it instead. Tagged [...]
Using HTML5 Elements Without JavaScript. on March 22nd, 2011 at 10:24 pm
I think it all depends what your target audience is.
In most of my projects I don’t mind using HTML5 tags as they are, and fix it with JS html5shim in IE. I understand it won’t cover those IE6-8 users which has their JS turned off, well but then I still can fix it a bit with css – it won’t be as great as with JS turned on but will still remain readable. I believe this approach should work for most of sites that do not target very wide audience.
However if I would build site (e.g. patients medical registry, world wide news ) that would need to be fully functional and appealing to e.g. older people or people in least developed countries then yes, first I would made sure it works on IE5-6 and after that I would look into modern browsers.
I’ve heard that BBC web team cares a lot about IE4, and kudos to them, they should!
Mariusz Nowak on March 23rd, 2011 at 7:05 am
Jesse, i think you got one too many R’s in there.
Also- I’m going with calling it the “html5 shiv” as it’s a better product name and there are many shims out there. Plus innerShiv kinda forced it.
The shiv in html5boilerplate is in modernizr which inherits basically all of html5shiv. The original by resig (and then remy sharp) was just the document.createElement loop but now the shiv takes printing as well (because printing ruins html5 elements).
Jon Neal wrote the printing handling which is pretty cool and tricky but it can be slow with a lot of CSS rules or a big DOM. Alexander Farkas recently rewrote it.. added a test suite.. sped things up a bunch..
And we’re soon putting that into the html5shiv (on google code) and baking it right into Modernizr 1.8.
(Wow I should write an article on the full narrative of this thing!)
I don’t like the (reverse-) bulletproof solutions because they break all sorts of traversal (the things Thierry highlighted).. but at the same time the JS based solution isn’t clean. It’s all just a big hack.
Paul Irish on March 23rd, 2011 at 6:12 pm
Hi Nicholas,
Great writeup. Thanks!
A couple of points I’d like to share:
I am not sure about that. I believe rather than ignoring $lt;section> altogether, IE “creates” an element for each tag. Something that looks more like this:
<div class=”outer”>
<section></section>
<h1>title</h1>
<p>text</p>
<section></section>
</div>
In short, childNodes on outer would return four items, not two (versus a single item in modern browsers).
You have a valid/solid point regarding the wrapping approach, but I believe it is related to existing documents. Imho, authors starting from scratch could choose to favor the nesting approach as they would not have to deal with the issues you mention and could benefit from a few advantages that come with the nesting technique, see: “Wrapping versus Nesting“.
Thierry Koblentz on March 23rd, 2011 at 11:15 pm
Another suggestion would be to serve up the Universal IE 6 stylesheet (or something similar) to older versions of IE using a
<noscript>tag in the head. Therefore, those versions of IE that don’t get html5shiv-ed will still get some nice looking styles.(A similar technique was mentioned on the WHATWG blog)
Thomas J Bradley on March 24th, 2011 at 12:54 pm
“It’s all just a big hack.”
Considering modernizr.js et alias, this last sentence amused me a lot. Especially in relation to its author.
KMB on March 24th, 2011 at 1:16 pm
I have created another approach using HTML5 shiv. I don’t use it now, but I will use it sometimes in the future.
alexander farkas on March 24th, 2011 at 3:14 pm
Nick,
Great article, but I have to disagree with your choice.
HTML5 is progressive. If a developer chooses to use it, then they shouldn’t use shim or excess div tags, it’s just simply against the idea. Html5 should help us make our documents semantic, and less reliant on DIV as they were in the past.
If the developer chooses to use the new tags, and wishes to support older browsers, then they should choose the shiv. Browsers which render html5 correctly shouldn’t be punished with excess tags. Using javascript to ‘assist’ older browsers is simply the lesser evil here.
In the end, sure, your method of choice stays true to HTML, but neither it or shiv is true to the ideas of HTML5.
Nouman on March 24th, 2011 at 8:47 pm
@Nouman – It’s Nicholas, not Nick.
I’m not sure I buy your argument about something being in the spirit of HTML5 or not. I don’t remember reading anything about HTML5 saying that you should just add in the tags and not worry about how older browsers will react. I can agree with you in principle that it would be nice to be able to do that, but practically speaking, I just don’t see how breaking your site for some of your users is a valid approach.
Nicholas C. Zakas on March 24th, 2011 at 11:23 pm
[...] thе well lονеd techniques fοr supporting HTML 5 іn legacy browsers, Using HTML 5 Semantic Elements Today, including whісh technique hе feels іѕ thе [...]
Web Development News March 2011 A at ralis.net on March 25th, 2011 at 3:03 am
Nicholas,
What benefit are these new tags then? Many books and articles are published on writing ‘efficient HTML’, and decreasing the DOM size through removing unnecessary elements. Wrapping everything in a div is more a step back then moving forward
Nouman on March 25th, 2011 at 12:32 pm
Well, reading it over again, I see that your objective is just to expose alternate methods to this problem. So, sorry for being an ass.
Nouman on March 25th, 2011 at 12:50 pm
Nicholas,
Great article as usual. But, here is something, I want to clarify –
Case 2:
“The tag is considered an error and a DOM node is created as a placeholder. The DOM is constructed as indicated by the code but the tag has no styles applied (considered an inline element).”
It says no styles are applied, but since it is treated as an inline element, all the styles applicable to inline elements will apply to it, right?
To vindicate the above point, I am copying the following paragraph -
Many browsers (such as Firefox 3.6 and Safari 4) will parse this as a top-level element with an unknown child element () that is created in the DOM but treated as an inline element. The and elements are children of . Because is represented in the DOM, it is possible to style the element. This is case #2.
The last line says it is possible to style the element. Am I misunderstanding or missing something here?
Thanks!
Vishal Gupta on March 26th, 2011 at 7:03 pm
Using HTML5 semantic elements today…
Over the past year, the argument over whether or not to use the new HTML5 semantic elements has morphed into how to use the new HTML5 semantic elements. All major browsers officially supporting these elements before the end of the year (many before the…
Shared on FAQPAL.com on April 3rd, 2011 at 9:21 am
Comments are automatically closed after 14 days.