createHtml()
For a little project I’m working on, I created a JavaScript function that can create HTML from (parsed) JSON data obtained through AJAX. I could just create the HTML on the server or from the JSON data in JavaScript, then inject it into the DOM with innerHTML(), but I wanted something more elegant. Now, the server creates specially formatted JSON, the browser fetches it though AJAX, it goes through JSON.parse(), and then my function creates the appropriate HTML, using W3C standard DOM methods to generate a document fragment, ready for insertion into the DOM. Alternatively, it can append its generated HTML directly to an existing element.
The code
The function and its helpers, made self containing for the purpose of this demo:
var createHtml = (function () {
// populate a lookup table: [object <Type>] => <type>
var objTypes = (function (types) {
var type, i = 0, ret = {};
while ((type = types[i++])) {
ret['[object ' + type + ']'] = type.toLowerCase();
}
return ret;
})(['Array', 'Boolean', 'Date', 'Function', 'Number', 'Object', 'RegExp', 'String']);
function type(obj) {
return obj === null || obj === undefined ?
String(obj) :
objTypes[Object.prototype.toString.call(obj)] || 'object';
}
function setStyle(elem, styleObj) {
var prop, elemStyle = elem.style || {};
for (prop in styleObj) {
if (styleObj.hasOwnProperty(prop)) {
elemStyle[prop] = styleObj[prop];
}
}
}
// create & return the actual function
return function createHtml(htmlArr, elem) {
var i = 0, item, prop;
// must be HTMLElement (1) or DocumentFragment (11)
if (!elem || !elem.nodeType || (elem.nodeType !== 1 && elem.nodeType !== 11)) {
elem = document.createDocumentFragment();
}
if (type(htmlArr) === 'array') {
while ((item = htmlArr[i++])) {
if (type(item) === 'string') {
elem.appendChild(document.createTextNode(item));
} else if (type(item) === 'object') {
// create appropriate element
elem = elem.appendChild(document.createElement(item.tagName || 'div'));
// set its properties
for (prop in item) {
if (item.hasOwnProperty(prop) && prop !== 'tagName') {
if (prop === 'css') {
setStyle(elem, item[prop]);
} else if (prop === 'text') {
elem.appendChild(document.createTextNode(item[prop]));
} else if (prop === 'children') {
// handle child nodes recursively
elem.appendChild(createHtml(item[prop]));
} else {
elem[prop] = item[prop];
}
}
}
// done, back to parent
elem = elem.parentNode;
} else {
// throw new TypeError('Expected string or object, instead saw ' + type(item));
}
}
} else {
// throw new TypeError('Expected array, instead saw ' + type(htmlArr));
}
return elem;
};
})();
Usage
The first argument should be an array containing objects and/or strings. Strings become text nodes in the document fragment, objects become HTML elements. The optional second argument should be an existing HTML element that will be appended to. In that case, the original, extended, element is returned. Otherwise, a document fragment is returned.
For objects in the array, the following properties, if present, have special meaning:
- tagName: Tag name for the HTML element, defaults to
div. - children: Another array containing strings and objects, will be handled recursively.
- css: An object with CSS
propery: valuepairs to be set on the HTML element’s style property. - text: To create an element containing only plain text. Shorthand for creating a children array containing a single string. Can theoretically be used in conjunction with a children array, but this is not advised.
All other properties are simply added to the HTML element. An example probably makes things more clear:
var html = [
{
tagName: 'a',
href: 'http://www.google.com/',
text: 'A link to Google.',
onclick: function () {
return window.confirm('Really wanna Google?');
}
},
{
id: 'testdiv',
className: 'one two three',
children: [
'Some text. ',
{
tagName: 'span',
css: {
color: 'red',
backgroundColor: 'yellow'
},
text: 'Red on yellow.'
},
' Some more text.'
]
},
'Even more text.'
];
If that array is then passed to createHtml() in one of two ways:
// for manipulation before insertion into the DOM
var frag = createHtml(html); document.getElementById('example').appendChild(frag);
// shorthand for direct insertion into the DOM
createHtml(html, document.getElementById('example'));
It will result in the following HTML being appended to the element with id="example":
<a href="http://www.google.com/">A link to Google.</a> <div id="testdiv" class="one two three"> Some text. <span style="color:red;background-color:yellow;">Red on yellow.</span> Some more text. </div> Even more text.
Demo
Using the the exact same code as shown above, employing the shorthand form (look directly above /* page specific JavaScript ends here */ in this page’s source):