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:
Concatenate strings
Create elements
Use a templating plugin
Generate the html on the server, then serve up via JSON.
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.
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.
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
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);
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.
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*/})
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>"
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.
Is there a best practice for this? I can see a few ways of doing it:
Concatenate strings
Create elements
Use a templating plugin
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...
Related
I currently have a bunch of lines that look like:
txt = "Can't print the value for <span class='keyword'>"+arguments[1]+"</span> before it's set";
I'm then doing
$('#mydiv').append($('<div/>').html(txt));
This looks terrible and I need to escape any html inside arguments[1]
The only alternative I can think of is to make sure all the text is inside its own element:
var spans = [];
spans[0] = $('<span/>').text("Can't print the value for ");
spans[1] = $('<span/>').text(arguments[1]).className('keyword');
spans[2] = $('<span/>').text(" before it's set");
$('#mydiv').append($('<div/>').append(spans[0],spans[1],spans[2]));
This is quite a lot for just a simple line of text. Is there anything else I can do?
edit: This isn't something that should be handled by a templating engine. It's html generated by a javascript logging function.
If It's a consistent format, I'd add it as a normal string and then do a search for the keyword part.
$('<div/>')
.appendTo('#mydiv')
.html("Can't print the value for <span class='keyword'></span> before it's set")
.find('.keyword').text(arguments[1]);
If you will be continuing to create lots of HTML using JS, I would suggest working with a templating library. I am a recent convert, it took me a long time to understand the point. But seeing many successful sites (twitter,github,etc.) and the great John Resig promote and/or make heavy use of templating, i'm glad I stuck with trying to understand. Now I get it. It's for separation of concerns, keeping logic out of the view.
I'm using this very bare bones templating library: http://blueimp.github.com/JavaScript-Templates/ though the templating provided by underscore.js and mustache.js are more popular.
The advantage of the library i'm using is its really small, <1kb and is basically like writing php/asp code if you are familiar with those.
you can write HTML inside <script> tags without having to escape:
using your variable, txt, the syntax looks like this:
<script>
var data={txt : "Can't print the value for <span class='keyword'>"+arguments[1]+"</span> before it's set"};
<div>{%=o.txt%}</div>
</script>
This problem is about Javascript writing HTML code for video player. I think there are some faster methods(document.createElement,Jquery and etc). Please tell some better and faster methods for this procedure. Thanks in advance
function createPlayer(videoSource){
document.writeln("<div id=\"player\">");
document.writeln("<object width=\"489\" height=\"414\" >");
document.writeln("<param name=\"player\" value=\"bin- debug/FlexPlayer.swf\">");
//etc
document.writeln("</embed>");
document.writeln("</object>");
document.writeln("</div>");
}
Going native with document.createElement will be the fastest. However, if your markup is large, going this way makes it a maintenance nightmare. Also, it is not easy to 'visualize' things.
In those cases, you might want to go for a tradeoff with client side templating solutions such as jQuery templates or underscore templates or John Resig's microtemplating.
Another performance boost is to build your entire markup and add it to DOM at the very end (add children first, then add the parent to DOM).
There is a jQuery function I know of that allows you to create a template HTML snippet which you can later use repeatedly with only 1 or 2 lines of code, adding in variables and appending it to the page.
For this you will need jQuery (latest should be fine) http://jquery.com/
Docs for the tmpl function are here: http://api.jquery.com/jquery.tmpl/
For details on how to use it you'd be best reading an example on the jQuery docs, I've not used it myself so can't write you a good example but there is great stuff on the docs site.
Hope this helps
EDIT:
A less resource intensive way to acheive that function would be to, rather than writing each line in turn to the document, just append them all to a string and then write that once when you are finished.
Eg:
function createPlayer(videoSource){
var html="<div id=\"player\">";
html+="<object width=\"489\" height=\"414\" >";
//etc
document.writeln(html);
}
This is faster because writing a line to the document uses more resources than just appending a string in memory. For MAXIMUM SPEED you could even declare the html var outside of the function and just set it to the markup as one long string, then write it - i.e
var html;
function createPlayer(videoSource){
html="<div id=\"player\"><object width=\"489\" height=\"414\" >"; //and so forth
document.writeln(html);
}
If you can justify the larger download sizes I'd go for the jQuery solution if possible, it's generally a bit more manageable - I've done plenty script generated HTML in the past and it very quickly becomes a pain to maintain. Good luck
You can try this:
function createPlayer(videosource){
var div = document.createElement('div');
div.innerHTML = '<object width=\"489\" height=\"414\" >' +
'.......'
document.body.appendChild(div);
}
For general manipulation and addition of HTML I'd recommend jQuery. It makes the process much easier and quicker.
You will find more information on this here:
jQuery Manipulation Methods - http://api.jquery.com/category/manipulation/
jQuery Tutorials - http://docs.jquery.com/Tutorials
I have some working Javascript code that generates an RDF/XML document using variables picked up from HTML fields:
...
peopleDoap += " <foaf:name>" + person_name + "</foaf:name>\n";
if (person_url != "") {
peopleDoap += " <foaf:homepage rdf:resource=\"" + person_url + "/\"/>\n";
}
if (person_pic != "") {
peopleDoap += " <foaf:depiction rdf:resource=\"" + person_pic + "/\"/>\n";
}
...
It's hard, looking at this code, to get any sense of what the output will look like (especially as this code is scattered amongst sub functions etc).
I'm wondering if there is an easy way that would enable me to have something like this:
...
<foaf:name>%person_name%</foaf_name>
<foaf:homepage rdf:resource="%person_url%"/>
<foaf:depiction rdf:resource="%person_pic%"/>
...
And then some substitution code. One slight complication is if fields are left blank, I will want to not generate the whole element. Ie, if person_url='', the above should generate as:
...
<foaf:name>%person_name%</foaf_name>
<foaf:depiction rdf:resource="%person_pic%"/>
...
I guess I could do this fairly naively by defining the template as a huge string, then performing a bunch of replaces on it, but is there anything more elegant? Mild preference for native Javascript rather than libraries, but happy to be convinced...
(Btw, yes, since this is RDF/XML, maybe there is a smarter way using some kind of RDF library. If you want to address that of the question instead, that's ok with me.)
Code is here.
Also, this is a widget running on a Jetty server. I don't think server-side code is an option.
I recommend using:
jQuery Templates
Mustache
John Resig's Micro-Templating
jQuery Templates are very powerful and nicely integrated with jQuery. That means that you can do things like this:
$.tmpl("Hello ${n}", {n: "World"}).appendTo('h1');
for the most simple stuff, or define templates in your HTML inside special script tags with custom MIME types, compile them, populate them with JSON data from AJAX calls, etc.
To add a bit of follow-up, I did implement John Resig's Micro-Templating (actually the refined version I posted earlier). However, I then backpedalled a bit. I found implementing control structures in the template is less readable than outside:
...
'<description xml:lang="en">#description</description>';
if (homepage) t +=
'<homepage rdf:resource="#homepage"/>';
...
rather than:
...
'<description xml:lang="en"><#= description #></description>' +
'<# if (homepage) { #>' +
'<homepage rdf:resource="<#= homepage =>"/>' +
'<# } #>';
...
I also ditched the microtemplating code for a simple substitution of variables, using #var rather than <# var #>.
Readability of templates like this is really critical, so I've done everything I could think of. In particular, keeping the javascript outside of the template lets syntax highlighting work, which is valuable to me.
That John Resig post also suggested burying the template in your HTML, in a but I preferred to keep it in my javascript, which is a separate .js.
Has anyone tried to use jQuery templates (or any other JavaScript based templating) on the server side with something like env.js?
I'm considering attempting it to see what benefits could be gained by being able to render identical templates on either the client or server side of a web application, but I was hoping someone might already have some experience, or know of an existing project doing this. I'd be particularly interested to know about any performance issues I might encounter compared to some more traditional templating engine.
To recap : Has anyone ever used jquery templates on the server site? If so, were there any performance issues, or other problems I might run into?
env.js is unnecessary.
<plug shameless="true">
I am in the process of specing and re-implementing JQuery templates to allow them to be used independently of the DOM. See https://github.com/mikesamuel/jquery-jquery-tmpl-proposal for code, and demos. The spec is available at http://wiki.jqueryui.com/w/page/37898666/Template and it says:
Text-centric rather than DOM dependent. Status: Done. See section 12 implementations. foo${bar} translates to something very similar to function (data, options) { return "foo" + bar; } modulo some dethunking of bar
...
This will allow to use this template engine in server side javascript environment, such as node.js, or java/rhino
I would love feedback and can help you get started.
</plug>
A friend of mine working on a distributed Genetic Programing project used a js sevrer side template system to manage all the web workers spawned across all users browsers. His code is here: github. I don't know how helpful it will be, but I know it was quite simple to implement and did some amazing things. From how easy he found it I would recommend a js template system it.
It's fairly trivial to write server side code to process the jQuery templates.
Here is some very basic vb.net code I have created that will return the result of a jquery template string to an array of any objects. Currently it only does the replacement of data values
Public Shared Function RenderTemplate(template As String, list As Array) As String
Dim myRegexOptions As RegexOptions = RegexOptions.Multiline
Dim myRegex As New Regex(strRegex, myRegexOptions)
Dim splits = myRegex.Split(template)
Dim matches = myRegex.Matches(template)
Dim i As Integer = 0
Dim swap As Boolean = False
Dim str As New StringBuilder
For Each item In list
swap = False
For i = 0 To splits.Length - 1
If swap Then
str.Append(CallByName(item, splits(i), CallType.Get, Nothing))
Else
str.Append(splits(i))
End If
swap = Not swap
Next
Next
Return str.ToString
End Function
So if I sent in the following...
Dim strTargetString As String = "<p><a href='${Link}'>${Name}</a></p>"
Dim data As New Generic.List(Of TestClass)
data.Add(New TestClass With {.Link = "http://stackoverflow.com", .Name = "First Object"})
data.Add(New TestClass With {.Link = "http://stackexchange.com", .Name = "Second Object"})
Return Render(strTargetString, data.ToArray)
It would output it as a string
<p><a href='http://stackoverflow.com'>First Object</a></p>
<p><a href='http://stackexchange.com'>Second Object</a></p>
This will work alot faster than spawning up a fake browser object on the server, and running the whole jQuery library just to replace a few tags.
With XML XSLT, it is possible to build a nice template in HTML, convert it to XSLT and use xml data coming from the server to dynamically populate widgets on the client side.
JSON and JSONP are great for interacting with the server side and manipulating the data in JS. When it comes to rendering the JSON data, most examples I've seen use JS to concatenate an ugly string and set the innerHTML of some element to render it.
Is there an easy browser compatible way of creating html widgets and populating them with JSON that doesn't involve string banging or building loads of DOM elements?
As mentioned in other answers, what you're looking for is a javascript based templating language. There is a pretty good list going in this related question. Just to highlight a couple, mustache is clean, simple and ported to many many languages. I believe its being used by Twitter. Google Closure has template language that works in both JavaScript and Java. That's been battle tested by Google obviously.
Other major JS libraries each have templating as plugins or part of the library. I know jQuery has at least one plugin and is planning one on the roadmap for v1.5. Dojo has a clone of Django's templating language that looks pretty nice.
There are others, but I think that's going to be the cream of the crop.
I don't actually use any of these, because I use a home grown one, but I can tell you that they are very nice to work with and I highly recommend them over string concatenation or doing more work on the server.
You should see this blog post by John Resiq: JavaScript Micro-Templating
It has a simple micro-templating code you can re-use. It goes like this:
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {};
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();
So you template would be in the markup:
<script type="text/html" id="item_tmpl">
<div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
<div class="grid_1 alpha right">
<img class="righted" src="<%=profile_image_url%>"/>
</div>
<div class="grid_6 omega contents">
<p><b><%=from_user%>:</b> <%=text%></p>
</div>
</div>
</script>
To use it:
var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);
To be clear - the above example was taken from the blog post and is not my code. Follow the link for more info.
You could use jQuery and some sort of jQuery Plugin for Templates. For example http://ivorycity.com/blog/jquery-template-plugin/ (have a look at this page, there are some nice samples)