Creating HTML templates in the context of Web programming is a very common task on both the server and client sides. Traditionally, on the client side, this task is assigned to libraries such as handlebars or mustache and similar. These libraries have defined a sort of standard in frontend programming. However, their internal behavior requires string interpolation, DOM building and rendering, and related work that can be avoided by adopting a native approach to templating.
A few years ago, HTML specifications introduced a native approach to creating templates on the client side based on the <template> element. However, this native approach seems not to be well known among frontend developers. Let’s take a closer look to understand how to use this approach and the benefits it brings to the table.
Detecting template support
Although most recent browsers widely support the <template> element, it is always good practice to check whether the browser you are using supports it. The typical approach is to create an instance of the element via DOM APIs and check if the content property is available, as shown in the following code:
if ('content' in document.createElement('template')) {
//...
}
If the <template> element is not supported, you can use your preferred templating library as a fallback.
Declaring a template
The <template> element allows us to declare a template, i.e., a structure of HTML tags we will fill with actual values at runtime. Defining a template is very easy. All you have to do is add a <template> element in the head or body of your HTML page and include some HTML in the starting and ending tags. The following is an example of template:
<template id="bookDetailTemplate">
<li>
<img src="">
<h5></h5>
<p></p>
</li>
</template>
This example defines a list item as the template for the markup that describes a book. The details of the book description consist of an image (<img>), a title (<h5>), and a text (<p>). Notice that an id has been assigned to the template: bookDetailTemplate. This id is needed to identify the template when we use it at runtime.
You can put the template element in the head section, or in the body of the HTML page – whichever is more convenient. In addition, the template element can contain any valid HTML markup, including <style> and <script> elements, and even the <template> element itself. However, the markup doesn’t need to represent a complete and working item. For instance, the template we defined above is not a complete list, but simply a list item. Also, the image and the other elements inside the template are not defined. Their value will be assigned at runtime, when the template will become a living branch of the DOM tree.
The main feature of the <template> element is its inertness. In fact, its content is hidden until the template is activated. Any content inside it is not loaded, nor rendered, nor executed. This means that any image contained in a template is not loaded, and no script is executed when the template’s content is parsed. The browser only checks the markup validity and creates a hidden DOM for it while the template is not in use. All of these elements become active only when the template is used. But how does that happen?
Using a template
Once a template is declared, you can use it with JavaScript in a few simple steps. First of all, you need to access the <template> element through the classic getElementById() method shown below:
const bookDetailTemplate = document.getElementById("bookDetailTemplate").content;
In this example, the internal content of the <template> element is actually identified by the bookDetailTemplate id. The content property provides the inert DOM structure generated by the markup inside the <template> element.
Once you have access to the template’s content, you can manipulate its DOM structure like any standard DOM. For example, you can access the image in our template with the following code:
const img = bookDetailTemplate.querySelector("img");
img.src = "images/myImage.jpg";
However, none of this comes to life until the template’s content is appended to the page’s DOM, as in the following example:
const bookList = document.getElementById("bookList");
bookList.appendChild(bookDetailTemplate.cloneNode(true));
Looking at the code above, you’reundoubtedly wondering why we have cloned the template’s content before appending it to the DOM. The answer is pretty simple; because you created a template, it’s very likely you would create multiple instances of DOM portions from it. If, on the other hand, you don’t clone the piece you are adding to the DOM, your manipulations will affect all the other instances you’ve already added. So this step is fundamental.
To illustrate a complete example of template use at runtime, consider the following code:
const bookData = [
{
cover: "...",
title: "...",
text: "..."
},
...
];
const bookDetailTemplate = document.getElementById("bookDetailTemplate").content;
const bookList = document.getElementById("bookList");
for (let bookDetail of bookData) {
const img = bookDetailTemplate.querySelector("img");
img.src = bookDetail.cover;
const title = bookDetailTemplate.querySelector("h5");
title.innerText = bookDetail.title;
const text = bookDetailTemplate.querySelector("p");
text.innerHTML = bookDetail.text;
bookList.appendChild(bookDetailTemplate.cloneNode(true));
}
In this example, we are mapping an array of objects, bookData, to the DOM by using the template declared before. Notice how we create an instance of a list item for each object using the template.
Summary
As you’ve seen in this article, creating and using an HTML template via the native <template> element is straightforward. The declaration and use of the template are not so different from the approach suggested by most common templating libraries. However, the native <template> element provides you with a few benefits. It allows you to leverage the native templating engine supported by most recent browsers with noticeable performance gain. In addition, it frees users from third party library dependency.