116

I'm calling a web service that returns an array of objects in JSON. I want to take those objects and populate a div with HTML. Let's say each object contains a url and a name.

If I wanted to generate the following HTML for each object:

<div><img src="the url" />the name</div>

Is there a best practice for this? I can see a few ways of doing it:

  1. Concatenate strings
  2. Create elements
  3. Use a templating plugin
  4. Generate the html on the server, then serve up via JSON.
Neuron
  • 5,141
  • 5
  • 38
  • 59
ckarbass
  • 3,651
  • 7
  • 34
  • 43
  • 1
    You could also check underscore js: http://documentcloud.github.com/underscore/#template It plays very nicely with backbone.js – luacassus Apr 18 '12 at 18:36
  • The choice mong 1-4: depends in how much content is to be injected.(prefer 4 for bigger) how many different html parts will need to be appended in total (3 or 4). what sb is familiar with. (Influence in dev time). If u dont know any tools its just a small modal that will be injected once I dont know amy better way than pure js to do it (1-2) – partizanos Dec 14 '16 at 18:19
  • html `string template` https://www.w3schools.com/js/js_string_templates.asp – Eric Nov 13 '21 at 06:27

8 Answers8

68

Options #1 and #2 are going to be your most immediate straight forward options, however, for both options, you're going to feel the performance and maintenance impact by either building strings or creating DOM objects.

Templating isn't all that immature, and you're seeing it popup in most of the major Javascript frameworks.

Here's an example in JQuery Template Plugin that will save you the performance hit, and is really, really straightforward:

var t = $.template('<div><img src="${url}" />${name}</div>');

$(selector).append( t , {
     url: jsonObj.url,
     name: jsonObj.name
});

I say go the cool route (and better performing, more maintainable), and use templating.

Chris
  • 684
  • 4
  • 12
Jim Fiorato
  • 4,842
  • 4
  • 28
  • 19
  • 15
    JQuery templating appears to be dead, see http://stackoverflow.com/questions/7911732/jquery-templates-are-deprecated – James McMahon Apr 22 '13 at 20:26
  • 4
    @Jim Fiorato: the link is dead :s – Adriano Feb 14 '14 at 16:32
  • 2
    Link is dead, as Adrien points out. Suggest you update your answer to include: [Mustache.js](https://github.com/janl/mustache.js) – Mr. Polywhirl Feb 17 '14 at 21:46
  • 2
    Can someone please explain, why an jQuery-based answer is the accepted one? I doubt that this is best practice! – ˈvɔlə Sep 11 '16 at 17:27
  • 1
    @WoIIe Even worse, the jQuery plugin is dead, so this answer is outdated. – Franklin Yu Dec 21 '16 at 01:55
  • 1
    10 years later you can do this with plain JS and it will work in any normal browser, using ticks for multi-line strings and template literals `\`

    ${title}

    \``
    – JoKr Jan 04 '20 at 19:19
14

If you absolutely have to concatenate strings, instead of the normal :

var s="";
for (var i=0; i < 200; ++i) {s += "testing"; }

use a temporary array:

var s=[];
for (var i=0; i < 200; ++i) { s.push("testing"); }
s = s.join("");

Using arrays is much faster, especially in IE. I did some testing with strings a while ago with IE7, Opera and FF. Opera took only 0.4s to perform the test, but IE7 hadn't finished after 20 MINUTES !!!! ( No, I am not kidding. ) With array IE was very fast.

some
  • 48,070
  • 14
  • 77
  • 93
  • I switched browser a long time ago, so I don't suffer that much. IE was a horrible browser but it is getting better. But I doubt I will ever switch back. – some Sep 25 '10 at 11:15
  • 1
    The slow performance seen in the first method is likely because the result string must be reallocated 200 times, and memory allocations can be slow. After two iterations you have "testingtesting". After three iterations, that string is thrown away and memory with enough room for "testingtestingtesting" is allocated. And so on 200 times with gradually increasing length. However s.join() allocates one new string as a result that's long enough to fit all of them, then copies in each one. One allocation, much faster. – EricP May 24 '14 at 00:11
  • 1
    @JoeCoder, agreed, its a Shlemiel The Painter algorithm. http://www.joelonsoftware.com/articles/fog0000000319.html – Jodrell Sep 23 '15 at 15:43
9

Either of the first two options is both common and acceptable.

I'll give examples of each one in Prototype.

// assuming JSON looks like this:
// { 'src': 'foo/bar.jpg', 'name': 'Lorem ipsum' }

Approach #1:

var html = "<div><img src='#{src}' /> #{name}</div>".interpolate(json);
$('container').insert(html); // inserts at bottom

Approach #2:

var div = new Element('div');
div.insert( new Element('img', { src: json.src }) );
div.insert(" " + json.name);
$('container').insert(div); // inserts at bottom
savetheclocktower
  • 1,443
  • 7
  • 12
  • Building generating the HTML explicitly with strings rather than DOM elements is more performant (assuming string concatenation isn't a real issue) and readable. – Rodrick Chapman Oct 21 '08 at 04:21
  • In IE string concatenation always is an issue. Use an array instead. – some Dec 07 '08 at 16:31
7

Here's an example, using my Simple Templates plug-in for jQuery:

var tmpl = '<div class="#{classname}">#{content}</div>';
var vals = {
    classname : 'my-class',
    content   : 'This is my content.'
};
var html = $.tmpl(tmpl, vals);
Andrew Hedges
  • 21,688
  • 16
  • 67
  • 79
7

Perhaps a more modern approach is to use a templating language such as Mustache, which has implementations in many languages, including javascript. For example:

var view = {
  url: "/hello",
  name: function () {
    return 'Jo' + 'hn';
  }
};

var output = Mustache.render('<div><img src="{{url}}" />{{name}}</div>', view);

You even get an added benefit - you can reuse the same templates in other places, such as the server side.

If you need more complicated templates (if statements, loops, etc.), you can use Handlebars which has more features, and is compatible with Mustache.

Tzach
  • 12,889
  • 11
  • 68
  • 115
5

You could add the template HTML to your page in a hidden div and then use cloneNode and your favorite library's querying facilities to populate it

/* CSS */
.template {display:none;}

<!--HTML-->
<div class="template">
  <div class="container">
    <h1></h1>
    <img src="" alt="" />
  </div>
</div>

/*Javascript (using Prototype)*/
var copy = $$(".template .container")[0].cloneNode(true);
myElement.appendChild(copy);
$(copy).select("h1").each(function(e) {/*do stuff to h1*/})
$(copy).select("img").each(function(e) {/*do stuff to img*/})
Leo
  • 2,860
  • 3
  • 24
  • 21
4

Disclosure: I am the maintainer of BOB.

There is a javascript library that makes this process a lot easier called BOB.

For your specific example:

<div><img src="the url" />the name</div>

This can be generated with BOB by the following code.

new BOB("div").insert("img",{"src":"the url"}).up().content("the name").toString()
//=> "<div><img src="the url" />the name</div>"

Or with the shorter syntax

new BOB("div").i("img",{"src":"the url"}).up().co("the name").s()
//=> "<div><img src="the url" />the name</div>"

This library is quite powerful and can be used to create very complex structures with data insertion (similar to d3), eg.:

data = [1,2,3,4,5,6,7]
new BOB("div").i("ul#count").do(data).i("li.number").co(BOB.d).up().up().a("a",{"href": "www.google.com"}).s()
//=> "<div><ul id="count"><li class="number">1</li><li class="number">2</li><li class="number">3</li><li class="number">4</li><li class="number">5</li><li class="number">6</li><li class="number">7</li></ul></div><a href="www.google.com"></a>"

BOB does currently not support injecting the data into the DOM. This is on the todolist. For now you can simply use the output together with normal JS, or jQuery, and put it wherever you want.

document.getElementById("parent").innerHTML = new BOB("div").insert("img",{"src":"the url"}).up().content("the name").s();
//Or jquery:
$("#parent").append(new BOB("div").insert("img",{"src":"the url"}).up().content("the name").s());

I made this library because I was not pleased with any of the alternatives like jquery and d3. The code very complicated and hard to read. Working with BOB is in my opinion, which is obviously biased, a lot more pleasant.

BOB is available on Bower, so you can get it by running bower install BOB.

Community
  • 1
  • 1
Automatico
  • 12,420
  • 9
  • 82
  • 110
  • You did a great job. :) Although I expect to invest time in understanding it's structure before applying in a complex scenario. But, it is the best solution in my opinion. – Imran Faruqi Dec 24 '20 at 04:45
  • 1
    Thanks @ImranFaruqi There has been a significant amount of time since I worked on the lib, but it should work as expected. Feel free to help out with it if it is useful for you! – Automatico Dec 30 '20 at 16:26
2

Is there a best practice for this? I can see a few ways of doing it:

  1. Concatenate strings
  2. Create elements
  3. Use a templating plugin
  4. Generate the html on the server, then serve up via JSON.

1) This is an option. Build up the html with JavaScript on the client side and then inject it in the DOM as a whole.

Note that there is a paradigm behind this approach: the server outputs just data and (in case of interaction) receives data from the client asyncronoulsy with AJAX requests. The client side code operete as a stand-alone JavaScript web application.

The web application may operate, render the interface, even without the server being up (of course it won't display any data or offer any kind of interaction).

This paradigm is getting adopted often lately, and entire frameworks are build around this approach (see backbone.js for example).

2) For performance reasons, when possible, is better to build the html in a string and then inject it as a whole into the page.

3) This is another option, as well as adopting a Web Application framework. Other users have posted various templating engines available. I have the impression that you have the skills to evaluate them and decide whether to follow this path or not.

4) Another option. But serve it up as a plain text/html; why JSON? I don't like this approach because mixes PHP (your server language) with Html. But I adopt it often as a reasonable compromise between option 1 and 4.


My answer: you are already looking in the right direction.

I suggest to adopt an approach between 1 and 4 like I do. Otherwise adopt a web framework or templating engine.

Just my opinion based on my experience...

Paolo
  • 15,233
  • 27
  • 70
  • 91