Cross-document communication with iframes

Using iframes (inline frames) is often considered bad practice since it can hurt you from a SEO point view (contents of the iframes will not be indexed by search engines). But whenever you have an application which doesn’t require indexing of contents (e.g. because the content is only visible after the user has been authenticated and authorized) or you need to embed content from other web sites/apps, iframes provide a nice mechanism to include content in your app and ensure that this doesn’t cause any major security issues.

Please refer to the MDN which contains a good description of iframes and a few examples.

Accessing an iframe and its content

The first step in building using iframes is of course to define an iframe tag in your HTML code which will define where in the DOM, the external resources will be taken over:

<iframe id="iframe1"></iframe>

Now you have added this tag in your HTML code, you will most probably want to access it with JavaScript to set a URL to be loaded, define how the iframe contents should be displayed (e.g. the width and height of the iframe) and maybe access some of the DOM elements in the iframe. This section will show you how this can be done.

Please keep in mind that things are relatively easy when working with iframes which contents are loaded from the same host/domain. If you work with contents from other hosts/domains, you’ll need to have a look at the next sections as well.

Setting the URL and styles of an iframe

Setting the URL which contents need to be loaded in the iframe, just means setting the src property of the iframe object. And styling it can be done by using the style property. Here’s a short example:

var iframe1 = document.getElementById('iframe1');
iframe1.style.height = '200px';
iframe1.style.width = '400px';
iframe1.src = 'iframe.html';

In this case the source for the iframe contents is an HTML page on the same host/domain but you could also define a complete URL pointing to another location.

Detecting when the iframe’s content has been loaded

Before you can access the contents of the iframe, you will have to wait for the iframe contents to be loaded (just like you should wait for the contents of your page to be fully loaded before accessing and manipulating them). This is done by defining an onload callback on the iframe:

iframe1.onload = function() {
    // your code here
}

Accessing the contents of the iframe

Once you’ve made sure that the iframe contents have been loaded, you can access it’s document using either the contentDocument property of the iframe object or by using the document property of the contentWindow property of the iframe. Of course, it’s just easier to use contentDocument. Unfortunately, it’s not supported by older versions of Internet Explorer so to make sure that it works in all browsers, you should check whether the contentDocument property exists and if not, revert to contentWindow.document:

var frameDocument = iframe1.contentDocument ? iframe1.contentDocument : iframe1.contentWindow.document;
var title = frameDocument.getElementsByTagName("h1")[0];
alert(title.textContent);

Interactions between iframe and parent

Now that you can load content in the iframe, define how it should be displayed and access its content, you might also need to go one step further and access the parent document (or the iframes properties) from the iframe itself.

Accessing the parent document

Just like we accessed the contents of the iframe from a script in the parent page, we can do the opposite (currently ignoring cross-domain issues) by using the document property of the parent object:

var title = parent.document.getElementsByTagName("h1")[0];
alert(title.textContent);

Accessing the iframe properties from the iframe

If you have some logic based on the styles of the iframe tag in the parent page (e.g. its width or height), you can use window.frameElement which will point you to the containing iframe object:

var iframe = window.frameElement;
var width = iframe.style.width;
alert(width);

Calling a JavaScript function defined in the iframe

You can call JavaScript functions defined in the iframe (and bound to its window) by using the contentWindow property of the iframe object e.g.:

iframe1.contentWindow.showDialog();

Calling a JavaScript function defined in the parent

Similarly, you can call a JavaScript function defined in the parent window by using the window property of the parent object e.g.:

parent.window.showDialog2();

Same Origin Policy

The Same Origin Policy is an important concept when using JavaScript to interact with iframes. This is basically a security policy enforced by your browser and preventing documents originating from different domains to access each other’s properties and methods.

What’s the same origin?

Two documents have the same origin, if they have the same URI scheme/protocol (e.g. http, https…), the same host/domain (e.g. google.com) and the same port number (e.g. 80 or 443).

So documents loaded from:

  • http://google.com and https://google.com do not have the same origin since they have different URI schemes (http vs https)
  • https://benohead.com and https://benohead.com:8080 do not have the same origin since they have port numbers (80 vs 8080)
  • https://benohead.com and https://www.benohead.com do not have the same origin since they have different hostnames (even if the document loaded from www.benohead.com would be the same if loaded from benohead.com)
  • http://kanban.benohead.com and https://benohead.com do not have the same origin since sub-domains also count as different domains/hosts

But documents loaded from URIs where other parts of the URI are different share the same origin e.g.:

  • https://benohead.com and https://benohead.com/path: folders are not part of the tuple identifying origins
  • https://benohead.com and http://user:password@benohead.com: username and password are not part of the tuple identifying origins
  • https://benohead.com and https://benohead.com/path?query: query parameters are not part of the tuple identifying origins
  • https://benohead.com and https://benohead.com/path#fragment: fragments are not part of the tuple identifying origins

Note that depending on your browser https://benohead.com and https://benohead.com:80 (explicitly stating the port number) might or might not be considered the same origin.

Limitations when working with different origins

A page inside an iframe is not allowed to access or modify the DOM of its parent and vice-versa unless both have the same origin. So putting it in a different way: document or script loaded from one origin is prevented from getting or setting properties of a document from another origin.

Interacting cross-domain

Of course, in most cases using iframes makes sense when you want to include contents from other domains and not only when you want to include contents from the same domain. Fortunately, there are a few options for handling this depending on the exact level of cross-domain interaction which is required.

URL fragment hack

What you would have done 5 to 10 years ago is workaround the limitation by using the fact that any window/iframe can set the URL of another one and that if you only change the fragment part of a URL (e.g. what’s after the hash sign #), the page doesn’t reload. So basically, this hack involves sending some data to another iframe/window, by getting a reference to this iframe/window (which is always possible), adding a fragment (or changing it) in order to pass some data (effectively using the fragment as a data container and setting the URL as a trigger event).

Using this hack comes with two main limitations:

  • This hack doesn’t seem to work anymore in some browsers (e.g. Safari and Opera) which will not allow child frame to change a parent frame’s location.
  • You’re limited to the possible size of fragment identifiers which depends on the browser limitation and on the size of the URL without fragment. So sending multiple kilobytes of data between iframes using this technique might prove difficult.
  • It may cause issues with the back button. But this is only a problem if you send a message to your parent window. If the communication only goes from your parent window to iframes or between iframes, then you won’t see the URL and bookmarking and the back button will not be a problem.

So I won’t go into more details as how to implement this hack since there are much better ways to handle it nowadays.

window.name

Another hack often used in the past in order to pass data from an iframe to the parent. Why window.name ? Because window.name persists accross page reloads and pages in other domains can read or change it.

Another advantage of window.name is that it’s very easy to use for storage:

window.name = '{ "id": 1, "name": "My name" }';

In order to use it for communicating with the parent window, you need to introduce some polling mechanism in the parent e.g.:

var oldName = iframe1.contentWindow.name;
var checkName = function() {
  if(iframe1.contentWindow.name != oldName) {
    alert("window name changed to "+iframe1.contentWindow.name);
    oldName = iframe1.contentWindow.name;
  }
}
setInterval(checkName, 1000);

This code will check every second whether the window.name on the iframe has changed and display it when it has.

You seem to be able to store up to 2MB of data in window.name. But keep in mind that window.name was never actually not designed for storing or exchanging data . So browser vendor support could be dropped at any time.

Server side proxy

Since the single origin policy is enforced by the browser a natural solution to work around it is to access the remote site from your server and not from the browser. In order to implement it, you’ll need a proxy service on your site which forwards requests to the remote site. Of course, you’ll have to limit the use of the server side proxy in order not to introduce an exploitable security hole.

A cheap implementation of such a mechanism, could be to use the modules mod_rewrite or mod_proxy for the Apache web server to pass requests from your server to some other server.

document.domain

If all you’re trying to do is have documents coming from different subdomains interact, you can set the domain which will be used by the browser to check the origin in both document using the following JavaScript code:

document.domain = "benohead.com";

You can only set the domain property of your documents to a suffix (i.e. parent domain) of the actual domain. So if you loaded your document from “kanban.benohead.com” you can set it to “benohead.com” but not to “google.com” or “hello.benohead.com” (although you wouldn’t need to set it to “hello.benohead.com” since you can set the domain to “benohead.com” for both windows/frames loaded from “kanban.benohead.com” and “hello.benohead.com”).

JSON with Padding (JSONP)

Although not directly related to the inter-domain and inter-frame communication, JSONP allows you to call a remote server and have it execute some JavaScript function define on your side.

The basic idea behind JSONP is that the script tag bypasses the same-origin policy. So you can call a server using JSONP and provice a callback method and the server will perform some logic and return a script which will call this callback method with some parameters. So basically, this doesn’t allow you to implement a push mechanism from the iframe (loaded from a different domain) but allows you to implement a pull mechanism with callbacks.

One of the main restrictions when using JSONP is that you are restricted to using GET requests.

Cross-Origin Resource Sharing (CORS)

CORS is a mechanism implemented as an extension of HTTP using additional headers in the HTTP requests and responses.

Except for simple scenarios where no extra step is required, in most cases enabling CORS means that an extra HTTP request is sent from the browser to the server:

  1. A preflight request is sent to query the CORS restrictions imposed by the server. The preflight request is required unless the request matches the following:
    • the request method is a simple method (i.e. GET, HEAD, or POST)
    • the only headers manually set are headers set automatically by the user agent (e.g. Connection and User-Agent) or one of the following: Accept, Accept-Language, Content-Language, Content-Type.
    • the Content-Type header is application/x-www-form-urlencoded, multipart/form-data or text/plain.
  2. The actual request is sent.

The preflight request is an OPTIONS request with an Origin HTTP header set to the domain that served the parent page. The response from the server is either an error page or an HTTP response containing an Access-Control-Allow-Origin header. The value of this header is either indicating which origin sites are allowed or a wildcard (i.e. “*”) that allows all domains.

Additional Request and Response Headers

The CORS specification defines 3 additional request headers and 6 additional response headers.

Request headers:

  • Origin defines where the CORS request comes from
  • Access-Control-Request-Method defines in the preflight request which request method will later be used in the actual request
  • Access-Control-Request-Headers defines in the preflight request which request headers will later be used in the actual request

Response headers:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Credentials
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers

How does it work?

The basic CORS workflow with preflight requests looks like this:

CORS

The browser send an HTTP OPTIONS request to the remote server with the origin of the page and the request method to be used. The remote server responds with an allowed origin and allowed methods headers. The browser then proceeds with the actual HTTP request. If you want to use some additional headers, an Access-Control-Request-Headers will also be sent in the OPTIONS request and an Access-Control-Allow-Headers will be returned in the response. You can then use this additional header in the actual request.

CORS vs. JSONP

Although CORS is supported by most modern web browsers, JSONP works better with older browsers. JSONP only supports the GET request method, while CORS also supports other types of HTTP requests. CORS makes it easier to create a secure cross-domain environment (e.g. by allowing parsing of responses) while using JSONP can cause cross-site scripting (XSS) issues, in case the remote site is compromised. And using CORS makes it easier to provide good error handling on top of XMLHttpRequest.

Setting up CORS on the server

In order to allow CORS requests, you only have to configure the server to add the following header to its response:

Access-Control-Allow-Origin: *

Of course, instead of a star, you can also return a single origin (e.g. benohead.com) or using a wildcard in the origin (e.g. *.benohead.com). This header can also contain a space separated list of origins. In practice, maintaining an exhaustive list of all allowed origins might be difficult, so in most cases you’ll either have a star, a single origin or a single origin and an origin containing a star e.g. benohead.com *.benohead.com. If you want to support a specific list of origins, you’ll have to have the web server check whether the provided origin is in a given list of allowed origins and return this one origin in the response to the HTTP call.

And if the requests to the web servers will also contain credentials, you need to configure the web server to also return the following header:

Access-Control-Allow-Credentials: true

If you are expecting not only simple requests but also preflight requests (HTTP OPTIONS), you will also need to set the Access-Control-Allow-Methods header in the response to the browser. It only needs to contains the method requested in the Access-Control-Request-Method header of the request. But usually, the complete list of allowed methods is sent back e.g.:

Access-Control-Allow-Methods: POST, GET, OPTIONS

Security and CORS

CORS in itself is not providing with the means to secure your site. It just helps you defining how the browsers should be handling access to cross-domain resource (i.e. cross-domain access). But since it relies on having the browser enforce the CORS policies, you need to have an additional security layer taking care of authentication and authorization.

In order to work with credential, you have set the withCredentials property to true in your XMLHttpRequest and the server needs put an additional header in the response:

Access-Control-Allow-Credentials: true

HTML5 postMessage

Nowadays, the best solution for direct communication between a parent page and an iframe is using the postMessage method available with HTML5. Using postMessage, you can send a message from one side to the other. The message contains some data and an origin. The receiver can then implement different behaviors based on the origin (also note that the browser will also check that the provided origin makes sense).

parent to iframe

In order to send from the parent to the iframe, the parent only has to call the postMessage function on the contentWindow of the iframe object:

iframe1.contentWindow.postMessage("hello", "http://127.0.0.1");

On the iframe side, you have a little bit more work. You need to define a handler function which will receive the message and register it as an event listener on the window object e.g.:

function displayMessage (evt) {
	alert("I got " + evt.data + " from " + evt.origin);
}

if (window.addEventListener) {
	window.addEventListener("message", displayMessage, false);
}
else {
	window.attachEvent("onmessage", displayMessage);
}

The parameter to the handler function is an event containing both the origin of the call and the data. Typically, you’d check whether you’re expecting a message from this origin and log or display an error if not.

iframe to parent

Sending messages in the other direction works in the same way. The only difference is that you call postMessage on parent.window:

parent.window.postMessage("hello", "http://127.0.0.1");

HTML5: Displaying the battery level

Note that this currently only works with Firefox. Support for Chrome and Internet Explorer is not yet available and it’s not yet known when it will be available.

The new Battery Status API allows you from a web page to access information about the battery status of the device rendering it. It is not only useful in order to show the current status but to implement battery status dependent logic e.g. increasing polling interval using Ajax or disabling animations in order to preserve the battery, or auto-saving in case the device goes out of power, prevent the user from performing long running operation which might not be able to finish because of the low remaining battery level.

First we’ll create the HTML and CSS in order to get an empty battery display looking like this:

The HTML code couldn’t be simpler. We just need a battery div:

<div id="battery"></div>

Now we need to display a border around the div and a smaller rectangle after it:

.battery:after {
    background-color: #fff;
    border: 2px solid #000;
    content: "";
    display: block;
    height: 16px;
    position: absolute;
    right: -6px;
    top: 6px;
    width: 6px;
}
.battery {
    background-color: #fff;
    border: 2px solid #000;
    height: 32px;
    margin-left: auto;
    margin-right: auto;
    position: relative;
    width: 100px;
}

Then we need to display a battery level within the battery display:

<div id="battery"><div id="battery-level"></div></div>

And we just need to add some CSS:

.battery .battery-level {
    background-color: #666;
    height: 100%;
}

By setting the width of .battery-level in pixels (or in percent which is the same since the element is 100 pixels wide) to the battery level in percents, we’ll get the right display:

Of course, one would expect our battery level display to show the level in a different color depending whether the battery is almost empty or not. So let’s introduce three CSS classes which will show the level in green, yellow or red:

Using the following CSS code:

.battery .battery-level.high {
    background-color: #66CD00;
}
.battery .battery-level.medium {
    background-color: #FCD116;
}
.battery .battery-level.low {
    background-color: #FF3333;
}

Before we move to the HTML5 part, let’s also display the battery level as text, by setting it as text on the battery-level element and by adding the following css rule:

.battery .battery-level {
    text-align: center;
}

It then looks like this:

10%
75%
95%

Now we’re almost done… All we need is what’s actually the main topic of this post: to get the battery level data using HTML5. For this we need to use the new Battery Status API.

The Battery Status API provides access the battery information through navigator.battery which implement the BatteryManager interface. This interface provides the following information:

  • whether the battery is currently charging
  • the charging time
  • the discharging time
  • the battery level

Additionally, it also provides events you can listen to:

  • onchargingchange
  • onchargingtimechange
  • ondischargingtimechange
  • onlevelchange

We will not use the events for now and just show the battery level when the page was loaded:

The JavaScript code to work with this API is pretty straight forward:

var battery = navigator.battery;
var level    = battery.level * 100;
var batteryLevel = jQuery('.battery .battery-level');
batteryLevel.css('width', level + '%');
batteryLevel.text(level + '%');
if (battery.charging) {
    batteryLevel.addClass('charging');
} else if (level > 50) {  
    batteryLevel.addClass('high');  
} else if (level >= 25 ) {  
    batteryLevel.addClass('medium');  
} else {  
    batteryLevel.addClass('low');  
}

If you want to update the battery level display as the battery charges and discharges:

Here’s the JavaScript code:

function updateBatteryDisplay(battery) {
	var level = battery.level * 100;
	var batteryLevel = jQuery('.battery .battery-level');
	batteryLevel.css('width', level + '%');
	batteryLevel.text(level + '%');
	if (battery.charging) {
	    batteryLevel.addClass('charging');
	    batteryLevel.removeClass('high');  
	    batteryLevel.removeClass('medium');  
	    batteryLevel.removeClass('low');  
	} else if (level > 50) {  
	    batteryLevel.addClass('high');  
	    batteryLevel.removeClass('charging');
	    batteryLevel.removeClass('medium');  
	    batteryLevel.removeClass('low');  
	} else if (level >= 25 ) {  
	    batteryLevel.addClass('medium');  
	    batteryLevel.removeClass('charging');
	    batteryLevel.removeClass('high');  
	    batteryLevel.removeClass('low');  
	} else {  
	    batteryLevel.addClass('low');  
	    batteryLevel.removeClass('charging');
	    batteryLevel.removeClass('high');  
	    batteryLevel.removeClass('medium');  
	}
}

var battery = navigator.battery;
updateBatteryDisplay(battery);
battery.onchargingchange = function () {
    updateBatteryDisplay(battery);
};
battery.onlevelchange = function () {
    updateBatteryDisplay(battery);
};

So the Battery Status API is a very powerful and useful API which is very easy to use. Now let’s hope it will soon be available in all major browsers !

HTML5: removed elements (tags)

Because of the semantical orientation of HTML5 many HTML tags have been declared a non-conformant (deprecated) in HTML5. These tags are tags which are either redundant or do not transport meaning but only display properties which should rather be done setting CSS properties than HTML tags.

Here’s the list of tags currently removed in HTML5:

Removed tag Usage Alternative HTML tags Alternative CSS properties
<acronym> acronym or abbreviation for a word <abbr>
<applet> Java applet <object>
<basefont> default font size for a document font, font-family, font-size, font-size-adjust, font-stretch, font-style, font-variant, font-weight, @font-face
<big> text font size one size bigger font-size
<center> center content horizontally within the containing element text-align
<dir> directory <ul>
<font> font size, color and face font, font-family, font-size, font-size-adjust, font-stretch, font-style, font-variant, font-weight, @font-face
<frame> area in which another HTML document can be displayed <iframe>
<frameset> container for <frame> elements
<noframes> element used when <frame> elements are not supported
<strike> strike-through <del> or <span> text-decoration
<tt> display text with monotype font <code>

Note that <u> was on the list of deprecated tags for some time but currently has found its way back into the HTML5 specification. <u> makes the contained text underlined. It's never been a good idea to use it since underlined text generally has a different meaning for users of HTML pages: it usually shows that this text is a link which is not the case with the <u> tag. Of course the same can be achieved with CSS, so this tag is not really required and using it for only display purposes doesn't fit the semantical objective of HTML5. This element only got out of the HTML5 non-conformity because there are scenarios in which it does transport a meaning (Chinese proper name mark, text marked incorrect by a spell-checker, indicate family names).

HTML5: New HTML5 semantic tags

Note: Eine deutsche Version des Artikels findet ihr bei amazingweb

In the early days of web development, HTML was used to define the content, structure and  appearance of a website.
With the introduction of CSS, there was a migration to a kind of Model-View pattern where CSS was responsible for the view i.e. the appearance and HTML only for the content and structure i.e. the model.
Using JavaScript (and especially with the many JavaScript libraries currently available), it has gradually evolved a Model-View-Controller pattern, where JavaScript has assumed the Controller role.
But the blending of content and structure was still there. The problem is not only that both content and structure are defined in HTML, but that there was no real way to separate or distinguish them.

The structural aspect of HTML, before HTML5, was usually reduced to a tree of div and span tags. This is nothing more than a grouping of parts of the site, which mostly results from the need to address these parts separately in CSS or JavaScript. So it was more a presentation or controller-driven organization of content, but in no case a semantically-related structuring.>

HTML5 now addresses this very issue. HTML becomes semantic. Specifically, it means that in HTML5 the different logical parts of a web page can now also be defined with dedicated HTML tags. Most websites have such a structure (or a relatively similar structure):

web site structure

Such a structure is typically encoded as:

<body>
    <div id="header">
        <h1>Titel</h1>
        <h2>Untertitel</h2>
    </div>
    <div id="navigation">
        <div id="menu_item1" class="menu_item">...</div>
        <div id="menu_item1" class="menu_item">...</div>
    </div>
    <div id="content">
        <div id="post1" class="post">...</div>
        <div id="post2" class="post">...</div>
        <div id="post3" class="post">...</div>
        <div id="post4" class="post">...</div>
    </div>
    <div id="sidebar">
        <div id="widget1" class="widget">...</div>
        <div id="widget2" class="widget">...</div>
        <div id="widget3" class="widget">...</div>
        <div id="widget4" class="widget">...</div>
    </div>
    <div id="footer">
    </div>
</body>

So there was already a semantic structuring of the web pages before HTML5. But there was no standardized structure, so everyone could do it in a different way and it was not possible to parse it universally.
The only truly semantic tags that were in HTML4 were the header tags (h1 to h6). They were unfortunately often understood as formatting tags, even if slowly their interpretation by search engines made them more semantical again.

New tags were defined in HTML5 to allow replacing most of the div container structure and encode web pages using a standardized page structure. In HTML5, the same page would look like this:

<body>
    <header>
        <hgroup>
            <h1>Titel </h1>
            <h2>Untertitel</h2>
        </hgroup>
    </header>
    <nav>
        <div id="menu_item1">...</div>
        <div id="menu_item1">...</div>
    </nav >
    <section>
        <article>...</article>
        <article>...</article>
        <article>...</article>
        <article>...</article>
    </section>
    <aside>
        <section>...</section>
        <section>...</section>
        <section>...</section>
        <section>...</section>
    </aside>
    <footer>
    </footer>
</body>

The nav tag can either be positioned within the header tag or outside. It depends on how exactly one understands the header tag.

The section tag is relatively similar to the div tag. The difference is that the section tag also means that the contents being combined are also linked thematically which was not always the case with the div tag.

The header tag can of course also be used in an article or section tag to define the title of an article or the widget name. It is also interesting to see that in modern browsers the appearance of h1 to h6 tags is different depending on what level they are defined in. The following:

<body>
    <section>
        <header>
            <h1>Articles</h1>
        </header>
        <article>
            <header>
                <h1>Article</h1>
            </header>
            <section>
                <h1>Section</h1>        
             </section>
         </article>
     </section>
</body>

Looks like this:

Different H1 sizes

These header tags have again a semantic meaning and are no longer pure formatting elements.

This new HTML5 structure is nice and semantically meaningful. But it has one drawback: Older browsers do not understand it. So often the two structures are combined. The older browsers simply ignore the new tags, they do not know and will only see the old structure. But newer browsers see both structures, which is ugly (but if you must support older browsers, you’re used to compromises anyway). Such a combined structure would look like this:

<body>
    <header>
        <hgroup>
            <div id="header">
                <h1>Titel</h1>
                <h2>Untertitel</h2>
            </div>
        </ hgroup>
    </header>
    <nav>
        <div id="navigation">
            <div id="menu_item1">...</div>
            <div id="menu_item1">...</div>
        </div>
    </ nav>
    <section>
        <div id="content">
            <article>
                <div id="post1">...</div>
            </article>
            <article>
                <div id="post2">...</div>
            </article>
            <article>
                <div id="post3">...</div>
            </article>
            <article>
                <div id="post4">...</div>
            </article>
        </div>
    </section>
    <aside>
        <div id="sidebar">
            <section>
                <div id="widget1">...</div>
            </section>
            <section>
                <div id="widget2">...</div>
            </section>
            <section>
                <div id="widget3">...</div>
            </section>
            <section>
                <div id="widget4">...</div>
            </section>
        </div>
    </aside>
    <footer>
        <div id="footer">
        </div>
    </footer>
</body>

Of course, with such a structure, the question is justified, whether it even makes sense to use the HTML5 semantic tags. There are basically 2 cases:

  • If you do not have to support older browsers (lucky you !), you can simply replace the old div tags with the new semantic tags.
  • If it is not the case, even though it is more work and the legibility suffers however from a functional perspective, you lose nothing compared to the old structure and the new HTML5 tags will slowly play an ever increasing role for search engines. With them it could be ensured that visitors arrive mostly because of the content on the page and not because there is something in the sidebar. The bounce rate would thus decrease.

Moving to the new HTML5 tags is recommended in the long term in any case, and is not a huge effort on a more technical side.

HTML5: time, datetime and pubdate

The HTML5 tag <time> addresses the need for semantically tagging a date or time. It allows defining both a machine readable and human readable version of a date or time. This way you can just have Yesterday displayed and let a machine parsing your page that it’s actually December, 7th 2012.

This is also of course useful for working with timezones. Let’s say you’re living in Germany but are on a business trip to India and write about doing something at 2pm. For the reader, it’s obvious that it’s 2pm Indian Standard Time but a machine will not understand that when you write 2pm, it usually means 2pm Central European Time but in this particular page/post it’s not the case. On the other hand, I don’t think that any human being likes reading date/times with time zone info (especially when it’s clear from the context which time zone is meant).

Using a <time> tag is as easy has writing <time>2011-12-25</time>. Of course, here you just tag “2011-12-25” as a date/time info but other than that it doesn’t really carry more info. If you do not use the datetime attribute, the content of the time tag must be a valid date/time i.e. contain a machine readable date/time.

Now, it really get’s interesting when I can write “last Christmas” and a machine parsing this understand I mean December, 25th 2011. That’s also really simple, just write <time datetime="2011-12-25">last Christmas</time>. This will be displayed as but will be interpreted by a machine as 2011-12-25.

Instead of a date, you could also have a time e.g. <time datetime="14:00">at 2pm</time> or a date and time e.g. <time datetime="2012-12-08T12:07+01:00">Now</time>.

If you specify a date and a time, you have to specify a UTC time offset or use “Z” for UTC time e.g. 2012-12-08T12:07+00:00 is the same as 2012-12-08T12:07Z.

You can also write a date omitting the year e.g. 12-08 for December 8th or a duration. Durations are defined by a P (for period of time) followed by the duration definition:

  • PnYnMnDTnHnMnS e.g. P3Y for three years or PT2H25M for 2 hours and 25 minutes
  • PnW e.g. P2W for 2 weeks

Note that P1M is a one month duration and PT1M is a one minute duration.

One restriction with <time> is that it can only represent in the Christian Era. Older date cannot be encoded. So if you’re writing an online biography of Julius Caesar, you can forget the <time> tag (except when referencing other books written about him later on).

Note that the <time> tag was removed from the HTML5 specification last year (to be replaced by the <data> tag) but made it again in HTML5 shortly afterwards. So it’s future is not 100% clear but at least it made it in WordPress and Drupal so it’s quite widely used.

The pubdate attribute is used to tell whoever is parsing the time tag that the time value is related to the time when article/document was published. To be exact, it indicates the date of publication of its nearest <article> ancestor if present or the entire document. There are good chances that pubdate will be removed from the HTML5 specification. That’s why HTML5 validators report its presence (and why I’ve removed it from my theme).