HTML Templates JavaScript polyfills

Robin Winslow picture Robin Winslow · Apr 17, 2013 · Viewed 8.9k times · Source

I'm looking for the most standards-compliant / future-proof method for front-end HTML templating.

There exists a relatively new W3C draft specification for HTML Templates, e.g.:

<template id="mytemplate">
    <img src="" alt="great image">
    <div class="comment"></div>
</template>

Does anyone know if any good JavaScript polyfills already exist to make <template> element usable in a cross-browser way? Preferably complying with this standard.


Difficulties

According the the HTML5Rocks guide these templates have the following properties:

  • "Its content is effectively inert until activated"
  • "Script doesn't run, images don't load, audio doesn't play,"
  • "Content is considered not to be in the document"
  • "Templates can be placed anywhere inside of <head>, <body>, or <frameset>"

I think it is impossible to implement all four of these properties purely with a JavaScript polyfill, so any solution would only be partial.

Answer

ivanreese picture ivanreese · Oct 15, 2015

Xotic750 offered a solid polyfill that works by mutating HTML elements — but it will fail if any new templates are later added to the DOM, and mutation is increasingly discouraged (where avoidable).

Instead, I recommend introducing the "polyfill" behaviour at the point where you use the templates. Add this function to your JS:

function templateContent(template) {
    if("content" in document.createElement("template")) {
        return document.importNode(template.content, true);
    } else {
        var fragment = document.createDocumentFragment();
        var children = template.childNodes;
        for (i = 0; i < children.length; i++) {
            fragment.appendChild(children[i].cloneNode(true));
        }
        return fragment;
    }
}

Call the function with a reference to your template element. It'll extract the content, and return a documentFragment that you can then attach to another element (or do whatever else you might want to do with the template content). Like this:

var template = document.querySelector("template#my-template");
var content = templateContent(template);
someElm.appendChild(content);

Now, the other answer didn't mention it, but you probably want some CSS to hide the <template> element.

template { display: none; }

Here's a CodePen that puts it all together.

Now, this will work correctly in browsers that natively support the <template> element, and in those that don't. Similar to the other answer, it's not a perfect polyfill, since it doesn't render templates inert (that'd be complex, slow, and error-prone). But it works well enough for me to use in production.

Leave a comment if you've got questions or issues, and I'll revise accordingly.