An Introduction to HTML Imports

HTML Imports

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": "http://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": "http://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": "http://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

Free Workshops

Watch one of our expert, full-length teaching videos. Choose from HTML, CSS or WordPress.

Start Learning

Comments

12 comments on “An Introduction to HTML Imports

  1. Very nice, didn’t realize this was coming. HTML includes have been high on my wish list for a long time. However, you can do this kinda thing today already if you use a code compiler like Hammer for Mac

  2. How does the browser render styles / scripts / images with relative path in the import and is in different location?
    | — index.html import’s `http://xyz.com/imports/importme.html`
    | — css/
    | — no-style.css

    | — xyz.com/imports/
    | — importme.html refer’s `css/style.css`
    | — css/
    | — style.css

    As per http://www.w3.org/TR/html-imports/#style-imports ,if there are multiple link to the same document, the first one should win. How is absolute path & relative path to the same document handled?

    • I wasn’t sure, so I’ve just tested this and any paths in the imported file are resolved relative to the main (importing) document.

      So say that your main document is on the domain `example-one.com` and imports a file from `http://example-two.com/templates.html` which contains a reference to `style.css`. The referenced file will be loaded relative to the main document domain so from `http://example-one.com/style.css`.

      If you’re planning on making an import available for people to use on other domains you should reference any additional resources using absolute paths.

      Hope this helps :)

  3. HTML imports is surely an exciting technological innovation of which claims to switch the way we assemble websites. Imports allow you to include HTML documents within other HTML documents.

    follow this link to get more information about our services
    http://arete.in/webdevelopment.aspx

  4. This is nice so you wouldn’t have to use php for accomplish this task. My question is does this create any security risk. You could potentially create a fake site to mimic another by importing it and collecting user data. Is there a way you can turn off the ability from an imported page to be imported.

    • It’s pretty easy for a site to be cloned even without HTML imports. I’m not aware of a way that you can prevent your page from being imported.

      • You can block your images from being hotlinked. Maybe there is or can be a way to prevent hot linking of your css and js files and also a way to prevent hotlinking of your actual pages when someone tries to use them in an html import context.

    • I too am unsure what benefit this method has over an AJAX-based import. If you’ve got to use JavaScript to interact with the import file, then what difference does it make having the link tag in the head of the page?

      Originally I thought maybe some form of prefetching… but then you can start an async AJAX call on DOM ready anyway – the difference is going to be negligible.

      I’ve read the W3C specification and it doesn’t really provide clarity over why this is of any benefit to anyone beyond simplifying AJAX a little.

      Can anyone see something a new use-case that I’m missing?

      • I think that if you’re already using Web Components it makes a lot of sense to go with HTML imports rather than AJAX. Especially so if you’re using templates to construct the UI.

        Other than that, I think there’s also a benefit to creating a bundle of code that can be imported through a single file.

        The reality is we’ve already found alternative ways to accomplish the majority of what HTML imports has to offer. HTML imports just provides a cleaner (IMO), standardised way of doing things.