[javascript] Defining a HTML template to append using JQuery

I have got an array which I am looping through. Every time a condition is true, I want to append a copy of the HTML code below to a container element with some values.

Where can I put this HTML to re-use in a smart way?

<a href="#" class="list-group-item">
    <div class="image">
         <img src="" />
    </div>
    <p class="list-group-item-text"></p>
</a>

JQuery

$('.search').keyup(function() {
    $('.list-items').html(null);

    $.each(items, function(index) {
        // APPENDING CODE HERE
    });
});

This question is related to javascript jquery json

The answer is


In order to solve this problem, I recognize two solutions:

  • The first one goes with AJAX, with which you'll have to load the template from another file and just add everytime you want with .clone().

    $.get('url/to/template', function(data) {
        temp = data
        $('.search').keyup(function() {
            $('.list-items').html(null);
    
            $.each(items, function(index) {
                 $(this).append(temp.clone())
            });
    
        });
    });
    

    Take into account that the event should be added once the ajax has completed to be sure the data is available!

  • The second one would be to directly add it anywhere in the original html, select it and hide it in jQuery:

    temp = $('.list_group_item').hide()
    

    You can after add a new instance of the template with

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
    
  • Same as the previous one, but if you don't want the template to remain there, but just in the javascript, I think you can use (have not tested it!) .detach() instead of hide.

    temp = $('.list_group_item').detach()
    

    .detach() removes elements from the DOM while keeping the data and events alive (.remove() does not!).


Other alternative: Pure

I use it and it has helped me a lot. An example shown on their website:

HTML

<div class="who">
</div>

JSON

{
  "who": "Hello Wrrrld"
}

Result

<div class="who">
  Hello Wrrrld
</div>

Very good answer from DevWL about using the native HTML5 template element. To contribute to this good question from the OP I would like to add on how to use this template element using jQuery, for example:

$($('template').html()).insertAfter( $('#somewhere_else') );

The content of the template is not html, but just treated as data, so you need to wrap the content into a jQuery object to then access jQuery's methods.


Use HTML template instead!

Since the accepted answer would represent overloading script method, I would like to suggest another which is, in my opinion, much cleaner and more secure due to XSS risks which come with overloading scripts.

I made a demo to show you how to use it in an action and how to inject one template into another, edit and then add to the document DOM.

example html

<template id="mytemplate">
  <style>
     .image{
        width: 100%;
        height: auto;
     }
  </style>
  <a href="#" class="list-group-item">
    <div class="image">
      <img src="" />
    </div>
    <p class="list-group-item-text"></p>
  </a>
</template>

example js

// select
var t = document.querySelector('#mytemplate');

// set
t.content.querySelector('img').src = 'demo.png';
t.content.querySelector('p').textContent= 'demo text';

// add to document DOM
var clone = document.importNode(t.content, true); // where true means deep copy
document.body.appendChild(clone);

HTML <template>

  • +Its content is effectively inert until activated. Essentially, your markup is hidden DOM and does not render.

  • +Any content within a template won't have side effects. Scripts don't run, images don't load, audio doesn't play ...until the template is used.

  • +Content is considered not to be in the document. Using document.getElementById() or querySelector() in the main page won't return child nodes of a template.

  • +Templates can be placed anywhere inside of <head>, <body>, or <frameset> and can contain any type of content which is allowed in those elements. Note that "anywhere" means that <template> can safely be used in places that the HTML parser disallows.

Fall back

Browser support should not be an issue but if you want to cover all possibilities you can make an easy check:

To feature detect <template>, create the DOM element and check that the .content property exists:

function supportsTemplate() {
  return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
  // Good to go!
} else {
  // Use old templating techniques or libraries.
}

Some insights about Overloading script method

  • +Nothing is rendered - the browser doesn't render this block because the <script> tag has display:none by default.
  • +Inert - the browser doesn't parse the script content as JS because its type is set to something other than "text/javascript".
  • -Security issues - encourages the use of .innerHTML. Run-time string parsing of user-supplied data can easily lead to XSS vulnerabilities.

Full article: https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old

Useful reference: https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode http://caniuse.com/#feat=queryselector

CREATING WEB COMPONENTS Creating custom web components tutorial using HTML templates by Trawersy Media: https://youtu.be/PCWaFLy3VUo


Old question, but since the question asks "using jQuery", I thought I'd provide an option that lets you do this without introducing any vendor dependency.

While there are a lot of templating engines out there, many of their features have fallen in to disfavour recently, with iteration (<% for), conditionals (<% if) and transforms (<%= myString | uppercase %>) seen as microlanguage at best, and anti-patterns at worst. Modern templating practices encourage simply mapping an object to its DOM (or other) representation, e.g. what we see with properties mapped to components in ReactJS (especially stateless components).

Templates Inside HTML

One property you can rely on for keeping the HTML for your template next to the rest of your HTML, is by using a non-executing <script> type, e.g. <script type="text/template">. For your case:

<script type="text/template" data-template="listitem">
    <a href="${url}" class="list-group-item">
        <table>
            <tr>
                <td><img src="${img}"></td>
                <td><p class="list-group-item-text">${title}</p></td>
            </tr>
        </table>
    </a>
</script>

On document load, read your template and tokenize it using a simple String#split

var itemTpl = $('script[data-template="listitem"]').text().split(/\$\{(.+?)\}/g);

Notice that with our token, you get it in the alternating [text, property, text, property] format. This lets us nicely map it using an Array#map, with a mapping function:

function render(props) {
  return function(tok, i) { return (i % 2) ? props[tok] : tok; };
}

Where props could look like { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

Putting it all together assuming you've parsed and loaded your itemTpl as above, and you have an items array in-scope:

$('.search').keyup(function () {
  $('.list-items').append(items.map(function (item) {
    return itemTpl.map(render(item)).join('');
  }));
});

This approach is also only just barely jQuery - you should be able to take the same approach using vanilla javascript with document.querySelector and .innerHTML.

jsfiddle

Templates inside JS

A question to ask yourself is: do you really want/need to define templates as HTML files? You can always componentize + re-use a template the same way you'd re-use most things you want to repeat: with a function.

In es7-land, using destructuring, template strings, and arrow-functions, you can write downright pretty looking component functions that can be easily loaded using the $.fn.html method above.

const Item = ({ url, img, title }) => `
  <a href="${url}" class="list-group-item">
    <div class="image">
      <img src="${img}" />
    </div>
    <p class="list-group-item-text">${title}</p>
  </a>
`;

Then you could easily render it, even mapped from an array, like so:

$('.list-items').html([
  { url: '/foo', img: 'foo.png', title: 'Foo item' },
  { url: '/bar', img: 'bar.png', title: 'Bar item' },
].map(Item).join(''));

Oh and final note: don't forget to sanitize your properties passed to a template, if they're read from a DB, or someone could pass in HTML (and then run scripts, etc.) from your page.


Add somewhere in body

<div class="hide">
<a href="#" class="list-group-item">
    <table>
        <tr>
            <td><img src=""></td>
            <td><p class="list-group-item-text"></p></td>
        </tr>
    </table>
</a>
</div>

then create css

.hide { display: none; }

and add to your js

$('#output').append( $('.hide').html() );

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to json

Use NSInteger as array index Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) HTTP POST with Json on Body - Flutter/Dart Importing json file in TypeScript json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 190) Angular 5 Service to read local .json file How to import JSON File into a TypeScript file? Use Async/Await with Axios in React.js Uncaught SyntaxError: Unexpected token u in JSON at position 0 how to remove json object key and value.?