HTML imports is an exciting technology that promises to change how we build websites. Imports allow you to include HTML documents within other HTML documents. This has a number of benefits, including the ability to create a bundle of HTML, CSS, and JavaScript code that can be shared within your application, or even with other applications on the web. Whilst it’s possible to achieve this using AJAX, HTML imports provide a much cleaner way to load self-contained components into your web pages.

In this blog post, you’re going to learn how to use HTML imports in your projects. You’ll also learn how to combine HTML imports with the power of the <template> element to create dynamic web pages.

Let’s get started!

Using HTML Imports

For starters, you should know that browser support for HTML imports is still pretty limited. Google Chrome has had support since version 31, but you still need to enable the feature manually.

enable-html-imports

To enable HTML imports in Chrome, go to chrome://flags and enable the Enable HTML Imports flag. Once you’re done, click the Relaunch Now button at the bottom of the screen to restart Chrome with support for HTML imports.

Now that you’re browser is all set up, let’s take a look at how you use imports within your web pages.

HTML imports use the <link> element to reference the file that you wish to import; this works in a similar way to how you include stylesheets. Make sure that you set the rel attribute to import to indicate to the browser that the referenced file should be imported into the document.

<head>
    <link rel="import" href="/path/to/template.html">
</head>

Note: HTML imports adhere to the same-origin policy for security reasons. Therefore, if you wish to import an HTML document from an external domain, you need to make sure you have CORS set up correctly.


As I mentioned earlier, HTML imports are great for bundling up other resources that are needed by your application. In the example below, the imported HTML file contains a number of <link> and <script> elements that are responsible for setting the base styling and functionality for an application.

<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/grid.css">
<link rel="stylesheet" href="css/alerts.css">    
<link rel="stylesheet" href="css/buttons.css">        
<script src="js/jquery.js"></script>
<script src="js/app.js"></script>

Importing this file into an HTML document would cause the browser to load each of these CSS and JavaScript files and then apply them to the main document.

Using Templates with HTML Imports

Now that you understand the basics of how HTML imports work, let’s take a look at how you can use this technology in tandem with the <template> element (also part of the Web Components gang) to load dynamic content into your web pages.

The first thing to know is that HTML imports don’t work like PHP includes. The HTML markup within the imported file won’t be automatically stamped out wherever you placed the <link> element. Instead, the imported HTML file (known as the import document) is loaded by the browser and stored behind the scenes and ready for you to use. You can then add the contents of the import document to your web page using JavaScript.


Note: This rule only applies to HTML content. Any CSS or JavaScript code will be loaded by the browser and applied the main document automatically.


You can access the contents of the import document by examining the import property of the corresponding <link> element.

var content = document.getElementById('myLinkElement').import;

Let’s say that we’re building a blog and want to have a page that displays a list of the most recent posts. We could create a template that is responsible for displaying the titles and snippets for each of the blog posts and store this template in a separate HTML file.

<template id="blog-post">
    <div class="post">
        <h2 class="post-title"><a href=""></a></h2>
        <p class="post-snippet"></p>
    </div>
</template>

Here we’ve created a simple HTML template with an <h2> for the post title and a <p> for the post snippet.

Next we need to import this file into the main document.

<head>
    <link rel="import" href="templates.html" id="templates">
</head>

Here we’ve added a simple <link> element to the <head> of our main document that references the HTML file containing the template (templates.html). We’ve also added an id attribute to the tag that will help us to reference the element using JavaScript.

Now we need to add an element to the main document that will contain the list of blog posts.

<div id="blog-posts"></div>

Next up, we need to create some dummy data for the blog posts that will be displayed in the list. To keep things simple, we’re just going to add an array of JavaScript objects directly to the main HTML file, but you could also load this data via AJAX.

<script>
  var blogPosts = [
    {
      "title": "Exploring the JavaScript Device APIs",
      "snippet": "The mobile revolution has completely changed how people access the web. It’s only natural then that the web should evolve to better suit the plethora of devices on which it’s now accessed.",
      "url": "https://blog.teamtreehouse.com/exploring-javascript-device-apis"
    },
    {
      "title": "Gamepad Controls for HTML5 Games",
      "snippet": "HTML5 games still have a lot of promises to fulfil, but the W3C gamepad specification is a great example of something that’s going well.",
      "url": "https://blog.teamtreehouse.com/gamepad-controls-html5-games"
    },
    {
      "title": "‘It Is Never Too Late’: How a Teacher and an Artist Switched to Web Careers in Midstream",
      "snippet": "The “breaking moment” in Jon Liu’s teaching career came at 7 o’clock in the morning.",
      "url": "https://blog.teamtreehouse.com/never-late-teacher-artist-switched-web-careers-midstream"
    }
  ];

  // TODO: Add the rest of the code here...
</script>

This data structure contains titles, snippets, and URLs for three separate blog posts.

Now it’s time to write the code that’s responsible to stamping out the blog post template.

First of all, you need to get the <link> element that references the imported HTML file and store it in a variable called templatesImport.

// Get the link element that references the templates.html file.
var templatesImport = document.getElementById('templates');

You can then access the import property on this variable to get the contents of the import document. Store this in a variable called templates.

// Retrieve the loaded templates.
var templates = templatesImport.import

Next, you need to find the <template> element within the HTML stored in the templates variable. You can do this using the getElementById method, passing in the id we gave the template (blog-post).

// Get the template.
var template = templates.getElementById('blog-post');

Now you’re ready to start stamping out the template for each of the blog posts.

You first need to create a for loop that will iterate over the list of blog posts.

// Repeat for each of the posts.
for (i in blogPosts) {
    // Get the current post.
    var post = blogPosts[i];

    // TODO: The rest of the code goes here...
}

The for..in structure used here will pass a variable, i, to the code block, which represents the current index within the blogPosts array. You then use this index to fetch the corresponding object from the blogPosts array and store this in a variable called post.

Next up you need to create a clone of the template’s content. To do this you use the document.importNode method, passing in the content of the template.

// Clone the template content.
var clone = document.importNode(template.content, true);

The boolean parameter (set here to true) specifies that the browser should also clone any descendent nodes within the template.

Now you need to apply the blog post data to the cloned template. You can do this by updating the innerText and href properties of the enclosed elements.

// Apply the blog post data to the template.
clone.querySelector('.post-title a').innerText = post['title'];
clone.querySelector('.post-title a').href = post['url'];
clone.querySelector('.post-snippet').innerText = post['snippet'];

The final step is to add the populated template to the list of blog posts in the main document. Remember that we gave this element the ID blog-posts.

// Add the blog post to the page.
document.getElementById('blog-posts').appendChild(clone);

That’s it! You now know how to use HTML imports and templates to create dynamic web pages.

For the remainder of this post you’re going to learn about import events and how to check that the user’s browser supports HTML imports.

Import Events

The <link> element has two events, onload and onerror, that can be used to monitor the loading status of your HTML imports. Attaching functions to these events allows you to execute code once the import file has been loaded, or to recover from any errors. At the moment, it’s best to do this using the onload and onerror attributes.

<script>
    // Handle Loaded Templates.
    function templatesLoaded(event) {
      console.log('Templates loaded.');
    }

    // Handle Errors.
    function templatesFailed(event) {
      console.log('Templates could not be loaded.');
    }
</script>

<link id="templates" rel="import" href="templates.html" onload="templatesLoaded(event)" onerror="templatesFailed(event)">

The browser will load the import file as soon as it encounters the <link>, so you need to make sure that your success and error handlers have been defined before the <link> element in your markup.


Note: This is one of the few occasions when using event attributes like onload and onerror is considered OK. Generally, you will want to take an unobtrusive approach to JavaScript when building your web applications.


Testing for Browser Support

You can check to see if the user’s browser supports HTML imports by looking for the import property on the <link> element.

if ('import' in document.createElement('link')) {
    // HTML imports are supported!
} else {
    // HTML imports are not supported.
}

The Polymer team has developed a great polyfill that adds support for HTML imports to other web browsers.

Final Thoughts

Just as containers revolutionised the shipping industry, the ability to share bundles of HTML, JavaScript, and CSS code in a form that is widely accepted is going to completely change how we approach frontend web development.

What are your thoughts on HTML imports and Web Components as a whole? Is this something you’re excited about or would you rather stick to a more traditional setup? Share your views in the comments.

Useful Links