19

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.

Supersharp
  • 29,002
  • 9
  • 92
  • 134
Robin Winslow
  • 10,908
  • 8
  • 62
  • 91

4 Answers4

11

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.

ivanreese
  • 2,718
  • 3
  • 30
  • 36
9

There is a jsfiddle that demonstrates such a polyfill.

<script>
    // Shim so we can style in IE6/7/8
    document.createElement('template');
</script>

<template id="example">
    <h1>
        This is template content.
    </h1>
    <p>
        It's really great.
    </p>
</template>


<div id="target">
    <p>
        This is regular old content.
    </p>
</div>

/* POLYFILL */
(function templatePolyfill(d) {
    if('content' in d.createElement('template')) {
        return false;
    }

    var qPlates = d.getElementsByTagName('template'),
        plateLen = qPlates.length,
        elPlate,
        qContent,
        contentLen,
        docContent;

    for(var x=0; x<plateLen; ++x) {
        elPlate = qPlates[x];
        qContent = elPlate.childNodes;
        contentLen = qContent.length;
        docContent = d.createDocumentFragment();

        while(qContent[0]) {
            docContent.appendChild(qContent[0]);
        }

        elPlate.content = docContent;
    }
})(document);

/* EXAMPLE */
var elExample = document.getElementById('example'),
    elTarget = document.getElementById('target');

elTarget.appendChild(elExample.content.cloneNode(true));

As for libraries, and I don't know that they support it yet, but try something like Modernizr and Initializr

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • Do you have a link to the jsfiddle? (although also it it's not advisable to create a [link only answer](http://meta.stackexchange.com/questions/84342/answer-that-only-contains-a-link-to-jsfiddle)). I am aware of Modernizr (Initializr just seems to include Modernizr for its polyfills) and it doesn't polyfill ` – Robin Winslow Apr 17 '13 at 09:22
  • The link is in my answer above, click on the text jsfiddle – Xotic750 Apr 17 '13 at 09:26
  • 1
    Your answer doesn't support the important requirement - security. ;) Once you put your template in a ` – mgol May 11 '14 at 22:15
  • You can simulate inertness via using a script tag with a custom mime type but then you don't get `.content`. – mgol May 11 '14 at 22:15
  • 1
    I don't use IE of any version, and while it is useful to point out security, the OP wanted: preferably a standards compliant polyfill but if not available then any solution close to it. A quote from Modernizr "just because you can use a polyfill doesn’t mean you should" – Xotic750 May 11 '14 at 22:31
  • 1
    Note that this polyfill works great for the main part, but if your template contains script tags, it will not prevent them from executing. In other words, don't use js scripts inside your templates if you care about ie11. – ling Oct 09 '16 at 12:19
4

Another possibility are the Web Components polyfills, which include a polyfill for the <template> tag. Keep in mind that webcomponents.js includes polyfills for more than just the <template> tag though.

donnapep
  • 500
  • 6
  • 19
  • 2
    If you only need a template polyfill, you just need to include https://github.com/webcomponents/webcomponentsjs/blob/master/src/Template/Template.js (does basically the same thing as the polyfill mentioned in other answers, but on domReady). – ernesto Oct 10 '16 at 15:11
  • @ernesto link's is dead, as webcomponent's template polyfill is now a stand-alone package and have it's own repository. Here it is:https://github.com/webcomponents/template – clemlatz Dec 19 '17 at 11:10
  • It's moved again: https://github.com/webcomponents/polyfills/tree/master/packages/template – JamesQMurphy Feb 24 '20 at 17:30
2

Yet another possibility is Neovov's Template-Element-Polyfill.

It's a dedicated <template> polyfill that you can find on Github.

Supersharp
  • 29,002
  • 9
  • 92
  • 134