61

Is there a general best practice for creating somewhat complex HTML elements in jQuery? I've tried a few different ways.

First I tried using createElement and chaining alot of those together with AppendTo and the like:

var badge = $(document.createElement("div")).attr("class", "wrapper1").appendTo("body");
$(document.createElement("div")).attr("class", "wrapper2").appendTo(".wrapper1");
$(document.createElement("table")).attr("class", "badgeBody").appendTo(".wrapper2");
$(document.createElement("tr")).attr("class", "row1").appendTo(".badgeBody");
$(document.createElement("td")).appendTo(".row1");
$(document.createElement("span")).attr("class", "badgeUnlocked").text("UNLOCKED! ").appendTo("td");
$(document.createElement("td")).attr("class", "badgeTitleText").appendTo(".row1");
$(document.createElement("span")).attr("class", "badgeTitle").text(name).appendTo(".badgeTitleText");
$(document.createElement("tr")).attr("class", "row2").appendTo(".badgeBody");
$(document.createElement("td")).appendTo(".row2");
$(document.createElement("img")).attr("src", imgUrl).appendTo(".row2 td");
$(document.createElement("td")).attr("class", "badgeText").appendTo(".row2");
$(document.createElement("span")).attr("class", "badgeDescription").text(description).appendTo(".badgeText");

This can be rough since appendTo wants to add to every matching element so everything needs its own name otherwise it ends up getting added repeatedly all over the place.

Then I tried creating an array and joining it together:

var badgeFragment = [
'<div><div id="'+ closeId+'" class="closeTab">X</div>',
'<div id="'+ badgeId+'" class="wrapper1">',
'<div class="wrapper2">',
'<div class="badgeBody">',
'<div class="badgeImage">',
'<img src="'+ imgUrl +'">',
'</div>',
'<div class="badgeContents">',
'<div class="badgeUnlocked">ACHIEVEMENT UNLOCKED: </div>',
'<div class="badgeTitle">'+ name +'</div>',
'<div id="'+ textId+'" class="badgeDescription">'+ description +'</div>',
'</div>',
'<div style="clear:both"></div>',
'</div></div></div></div></div>',
]
badgeFragment = $(badgeFragment.join(''));

This seems to work pretty well, although in IE when I would put an alert($(badgeFragment).text()) it usually came back empty. (This was part of debugging a larger problem). I'm obviously a bit new to jQuery (And even Javascript really) so to try and make sure this wasn't the problem I tried a third method - giant string concatenation:

var badgeFragment =
'<div><div id="'+ closeId+'" class="closeTab">X</div>' +
'<div id="'+ badgeId+'" class="wrapper1">' +
'<div class="wrapper2">' +
'<div class="badgeBody">' +
'<div class="badgeImage">' +
'<img src="C:/Users/Ryan/Documents/icons/crystal_project/64x64/apps/chat.png">' +
'</div>' +
'<div class="badgeContents">' +
'<div class="badgeUnlocked">ACHIEVEMENT UNLOCKED: </div>' +
'<div class="badgeTitle">test</div>' +
'<div id="'+ textId+'" class="badgeDescription">test</div>' +
'</div>' +
'<div style="clear:both"></div>' +
'</div></div></div></div></div>';

Is one of these methods generally considered better than the others? I'm not really good with the various profilers so I'm not sure how to verify this myself. There is also the question of how whether or not all of these methods are cross browser compliant.

Deduplicator
45.9k7 gold badges73 silver badges125 bronze badges
asked Feb 7, 2010 at 16:46
1
  • 1
    Your alert in IE came up empty because ".text()" doesn't get all the contents of your DOM subtree. Try $(badgeFragment).html() Commented Feb 7, 2010 at 16:52

9 Answers 9

67

With jQuery 1.4, you can create HTML elements like so:

// create an element with an object literal, defining properties
var e = $("<a />", {
 href: "#",
 "class": "a-class another-class", // you need to quote "class" since it's a reserved keyword
 title: "..."
});
// add the element to the body
$("body").append(e);

Here's a link to the documentation.

I'm not sure that this approach is faster than using the html() function of jQuery. Or faster than skipping jQuery all together and use the innerHTML property on an element. But as far as readability goes; the jQuery-approach is my favorite. And in most cases the performance-gain of using innerHTML is marginal.

answered Feb 7, 2010 at 17:07
Sign up to request clarification or add additional context in comments.

6 Comments

You missed a comma after the first argument of jQuery ($).
@roosteronacid depending on the jQuery docs The name "class" must be quoted in the object since it is a JavaScript reserved word.
@Peter yep. Goes for all the reserved words. Updated my answer.
How to declare nested element with this approach?
+1 for aiming for readability. That's always wise because CPU speed tends to increase over time, while understanding of the code decreases.
|
12

You don't have to call document.createElement:

$('#existingContainer').append(
 $('<div/>')
 .attr("id", "newDiv1")
 .addClass("newDiv purple bloated")
 .append("<span/>")
 .text("hello world")
);

There are all sorts of useful tools in jQuery for extending/ammending the DOM. Look at the various "wrap" methods for example.

Another possibility: for really big blobs of new content, you may be better off having your server prepare those (using the server-side templating system, whatever that is for you) and fetching those with $.load() or some other ajax approach.

answered Feb 7, 2010 at 16:49

Comments

10

Since HTML5 there's the <template> tag.

$(function () {
 // Get template
 var template = $("#template").html();
 // Create a new row from the template
 var $row = $(template);
 // Add data to the row
 $row.find("td[data-template='firstName']").text("Foo");
 $row.find("td[data-template='lastName']").text("Bar");
 // Add the row to the table
 $("#table").append($row);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<template id="template">
 <tr>
 <td data-template="firstName"></td>
 <td data-template="lastName"></td>
 </tr>
</template>
<table id="table">
 <tr>
 <th>Firstname</th>
 <th>Lastname</th>
 </tr>
</table>

answered Sep 12, 2018 at 1:09

1 Comment

The most elegant way IMO.
8

I'd be inclined to look at one of the templating engines for jquery like jQote

answered Feb 7, 2010 at 16:58

1 Comment

+1. or jsRender, or underscore - there are a whole lot of choices, and they're much better esp because Javscript doesn't support multi-line strings and sprintf kind of parameter replacement functions
8

John Resig (creator of jQuery) suggested using this templating method back in 2008. It's actually got some pretty cool features:

<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><a href="/<%=from_user%>"><%=from_user%></a>:</b> <%=text%></p>
 </div>
 </div>
</script>

then retrieving it using...

var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);

See here for full details:
http://ejohn.org/blog/javascript-micro-templating/

answered Sep 1, 2011 at 0:23

2 Comments

ICanHaz looks great, @MarkAmery, but it just wasted a couple hours of my time before I finally figured out that either it or Mustache templating doesn't handle certain characters very well, such as backslash \ . I can't afford this waste of time!
@ErikE Ouch. I'm pretty surprised that a library with as much activity as icanhaz has would be broken in any significant way. Any chance you can provide a minimal demo case in a jsfiddle? I might have a play and see if I can fix the bug.
5

It is more readable and maintainable if the JavaScript code resembles the HTML tag structure. You can go the official jQuery route using $('div', { 'class': 'etc'})...

Here is a slightly different approach using a function $$ to make each element an editable jQuery object. It should be pretty fast as there is no html parsing taking place.

$$('div', {'id':'container'},
 $$('div', {'id':'my_div'},
 $$('h1',{'class':'my_header'},
 $$('a',{'href':'/test/', 'class':'my_a_class'}, 'teststring'))));

This makes the approach more flexible and you can add event handlers, data etc. to the nested jQuery objects using chaining quite easily e.g.

$$('div', {'id':'container'},
 $$('div', {'id':'my_div'},
 $$('h1',{'class':'my_header'},
 $$('a', { 'href': '/test/', 'class': 'my_a_class' }, 'teststring')
 ).click(function() { alert('clicking on the header'); })
 ).data('data for the div')
).hide();

The code is more readable than if one were to use the official jQuery approach of doing it with separate calls to .append(), .text(), .html() etc. or by feeding the jQuery $ a concatenated HTML string.

Reference function $$:

function $$(tagName, attrTextOrElems) {
 // Get the arguments coming after the params argument
 var children = [];
 for (var _i = 0; _i < (arguments.length - 2) ; _i++) {
 children[_i] = arguments[_i + 2];
 }
 // Quick way of creating a javascript element without JQuery parsing a string and creating the element
 var elem = document.createElement(tagName);
 var $elem = $(elem);
 // Add any text, nested jQuery elements or attributes
 if (attrTextOrElems) {
 if (typeof attrTextOrElems === "string") { // text
 var text = document.createTextNode(attrTextOrElems);
 elem.appendChild(text);
 }
 else if (attrTextOrElems instanceof jQuery) { // JQuery elem
 $elem.append(attrTextOrElems);
 }
 else // Otherwise an object specifying attributes e.g. { 'class': 'someClass' }
 {
 for (var key in attrTextOrElems) {
 var val = attrTextOrElems[key];
 if (val) {
 elem.setAttribute(key, val);
 }
 }
 }
 }
 // Add any further child elements or text 
 if (children) {
 for (var i = 0; i < children.length; i++) {
 var child = children[i];
 if (typeof child === "string") { // text
 var text = document.createTextNode(child);
 elem.appendChild(text);
 } else { // JQuery elem
 $elem.append(child);
 }
 }
 }
 return $elem;
}
answered Oct 10, 2016 at 21:50

Comments

3

I personally think that it's more important for the code to be readable and editable than performant. Whichever one you find easier to look at and make changes to without breaking it should be the one you choose.

answered Feb 7, 2010 at 16:49

Comments

3

I like the templating language Handlebars.js

answered Apr 14, 2014 at 16:36

Comments

-1

The way I am doing is shown bellow. I am not sure if u should create so many divs but it worked pretty well with me.

var div1 = $('<div class="colwrap_fof"></div>'); //THE COMPLEX DIV ITSELF
var div2 = $('<div class="homeimg"></div>'); 
var div21 = $('<div id="backImageDiv'+fof_index+'" class="backDiv"></div>');
var div22 = $('<div id="frontImageDiv'+fof_index+'" class="frontDiv"></div>');
div2.append(div21);
div2.append(div22);
div1.append(div2);
$("#df_background").append(div1); // ADDING the complex div to the right place 

Cheers,

answered Jan 5, 2013 at 20:58

2 Comments

Hi @marcelosalloum, from my experience this is absolutely painful when someone says I need you to now add in <insert whatever here> to your code.. Yes, it does work, and I can't comment on performance, but it's very painful to maintain..
If your aim is to merely create a template and then fill it in with some data, I personally like to just make a display:none; container for my template elements, fetch them into javascript variables using either getElementById() or jQuery, clone it, then handle data filling myself (.textContent=val/.text(val), .setAttribute(attr,val)/.attr(attr,val)) because it is easier to do it myself than worry whether some shoddy templating library will do its job correctly and far more performant because I know exactly what I want to do with the template.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.