Load HTML template with JavaScript

JavascriptHtmlTemplates

Javascript Problem Overview


I am struggling to find a clean solution to my problem and was wondering if anyone could offer some tips.

I have "templates.html" which contains a collection of HTML snippets which I want to load into JavaScript and use. What is a good way to access the templates/snippets bearing in mind that templates.html is not a loaded DOM document?

I was thinking about using document.open to create a DOM to access but I think this has issues on certain browsers.

Javascript Solutions


Solution 1 - Javascript

Use jQuery and the .load() ( http://api.jquery.com/load/ ) method to inject the loaded document into the DOM.

$(function() {
    $('#content').load('/templates.html');
});

Solution 2 - Javascript

This is a bit old but since "Load HTML template with JavaScript" nowadays should refer to the loading of a HTMLTemplateElement here is a more modern looking approach to loading natives templates with javascript that also works for your use case.

First of all using <template> is already better than loading HTML into a hidden DIV because templates are innert and don't display content. You can have the templates being rendered from the beginning and use them whenever you need.

<html>
<head>
  <template>My template</template>
</head>
<body>
  <script>
  document.body.append(
    document.importNode(
      document.querySelector('template').content,
      true
    )
  )
  </script>
</body>
</html>

Or create them dynamically.

const template = document.createElement('template')
// modify the template's content
template.content.append(document.createElement('div'))
// add it to the document so it is parsed and ready to be used
document.head.append(template)

Because we want the content of the template to be built based on some text we get from the network, we have to parse it and add it to our template.content.

const text = fetchTemplateSomehowAsText('my-template.html')
const parsedDocument = new DOMParser().parseFromString(text, 'text/html')
template.content.append(parsedDocument.querySelector('#my-snippet'))

If my-template.html already comes wrapped in the <template> tag we can avoid the part of creating the template element manually because the DOMParser already creates the template element for us.

document.head.append(
  new DOMParser().parseFromString(text, 'text/html')
    .querySelector('template')
  )
)

This is a snippet I've been using to load templates into the document dynamically that uses what I just explained.

Solution 3 - Javascript

You can load the html into a hidden div and then you will have a DOM access the easiest way to load the html to a DIV is using jquery load: http://api.jquery.com/load/

$( "#result" ).load( "ajax/test.html" );

Solution 4 - Javascript

another way to do this is using requireJS.

require (['text!template_name'], function(yourTemplate) {
  // do stuff in here with yourTemplate dinamically load
}

Solution 5 - Javascript

For simple requiring you can try:

var xhr = new XMLHttpRequest(); 
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {     
        //do something with xhr.responseText
    }   
};      
xhr.open('GET', '/template.html');
xhr.send();  

Solution 6 - Javascript

you could load the template async using jquery ajax

$.get("/html/template01.html")
.done((data) => {
    console.info(data); // output the content of the html file
});

Solution 7 - Javascript

EDIT NOTE: if you are using ES6 go the 3rd edit directly

What about reading the files which contains your HTML templates after loading the page and load them in sessionStorage or localStorage as string value under appropriate names ?

Then you can just access your template in javascript by using the name or key specified for that template on sessionStorage or localStorage depending on which you choose.

(instead of using sessionStorage or localStorage you could preserve their stockage capabilities by designing an object used in global scope that will allow you to store templates and serve them on demand )

This is just theoretic I haven't tried it yet but one of the advantages I can see for this method is that you will not have your template code displayed in the page source.

Edit:

Going practical we can achieve this like the following.

Using fetch instead of jQuery you will have something like this :

var fileURL = "URL of the file to load";

fetch(`${fileURL}`).then(function(response) {
        response.text().then(function(text) {
                console.log(text); // output the file content as text
                // put data in localStorage or sessionStorage here
                localStorage.setItem("my-template", text);
        })
});

Now if you want your code to be executed only when localStorage["my-template"] exists do it that way:

if (localStorage["my-template"]) {
        console.log("'my-template' exist");
        // here you put the code that needs "my-template"
} else {
        console.log("error: could not find 'my-template'");
}

Now you can access "my-template" anywhere from javascript using :

localStorage.getItem("my-template");

Note that if you want to avoid errors the generation of your custom elements that use "my-template" should be done inside:

if (localStorage["my-template"]) { ... }

Also some minors changes may be required prior to data usage probably a simple text formatting operation. As the data-string contained in localStorage appears to contains text formatting characters. When applying the text as innerHTML for any element in the page it behaves correctly despite the text formatting chars it contains.

Given the fact that you will only get the content of your file when the response is resolved.

You can start the processing related to that file by :

  • sending an event that you will attach to a function for processing
  • calling a function that will perform the processing directly

Those needs to be done or initiated inside this part of the fetch instruction :

response.text().then(function(text) { ... })

EDIT 2:

If you do not want to put your templates code in the page you will need to remove the <template> tags that encapsulate them and add the resulting code as the innerHTML of the shadow DOM on the customElements that require the templates.

Here is a short example :

class TestCustomElement extends HTMLElement {
	
	constructor() {
		super(); // always call first !!

		// create and attach a new shadow DOM to the element
		this.shadow = this.attachShadow({mode:"open"});  
		
        // we define the apparence of our customElement by setting it's shadow DOM innerHTML property
		this.shadow.innerHTML = `<style> 
			.container {
				width:fit-content;
				color:black;
				background-color: yellow;
				border: 1px solid red;
				padding:5px;
			}
			
			span {
				padding:0px 10px 0px 10px;
			}
		</style>
		
		<div class="container">
			<span>"Hi I'm a test span !"</span>
		</div>`; 

       // here you put the functionalities	
	}
    
    // here you put the callbacks
}

customElements.define("test-custom-el", TestCustomElement); 

If you want to try this there is this codepen or you can save the above code as a .js file. Then you can load that file into the header of your test page by using this:

<script src="FileName.js" defer></script>

To add one customElement of that class just add those tags in the body of your test page :

<test-custom-el></test-custom-el>

Now if you want to combine this with the fetch example of this post you will have to remove the <template> tags from your templates files and set the loaded content as the value for the innerHTML of the shadow DOM for the customElements that will use them.

This way there's no need to add <template> code into your page.

Edit 3 :

BEST WAY USING ES6 :
After some more tests I figured it out that the best way to import an .html file in a .js file using ES6 is to include a .js file instead of a .html one this way it allows the use of the import / export functionalities pretty easily without having to deal with some more heavy async operations that are required to retrieve an html file in a javascript ES6 file.

Here is a short example on how to perform that :
for this example we assume that you have a "template.html" file in the same directory as a "module.js" file.

First You need to create a "template.js" file from "template.html" in which you put the following :

export default `Put the content of template.html here`;

You can create a little script to create those files for you.

When you have this "template.js" file you can now import it in your "module.js" file by simply using this import statement:

import template from './template.js';

Then you can use it to assign the template value of your module in it's constructor this way :

this.template = new DOMParser().parseFromString(template, 'text/html').querySelector('template');

this.template will now contain a <template> element as described in the "template.js" file (the element is created by the DOMParser).

In short your "module.js" file should look like this :

import template from './template.js';

export default class MyModule extends HTMLElement {

        constructor() {
            super();
            this.shadow = this.attachShadow({mode:"open"});
            this.template = new DOMParser().parseFromString(template, 'text/html').querySelector('template');
            this.shadow.appendChild(this.template.content);
        }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioniancrowtherView Question on Stackoverflow
Solution 1 - JavascriptpeterpView Answer on Stackoverflow
Solution 2 - JavascriptolanodView Answer on Stackoverflow
Solution 3 - JavascriptkleinohadView Answer on Stackoverflow
Solution 4 - JavascriptAndrei-Niculae PetreView Answer on Stackoverflow
Solution 5 - JavascriptIvanMView Answer on Stackoverflow
Solution 6 - JavascriptNerdroidView Answer on Stackoverflow
Solution 7 - JavascriptDarkosphereView Answer on Stackoverflow