Click here to Skip to main content
Licence CPOL
First Posted 26 May 2011
Views 7,378
Downloads 66
Bookmarked 4 times

JsonML Simplified - Array.prototype.toDomNodes

By John Doherty | 26 May 2011
A new JavaScript method to make JsonML a viable option for your projects
   4.50 (3 votes)

1

2

3
1 vote, 33.3%
4
2 votes, 66.7%
5
4.50/5 - 3 votes
μ 4.50, σa 1.01 [?]
 

Introduction

The code presented in this article allows DOM elements to be created from a JSON object formatted as markup (JsonML) via one function call. This means you can have your Ajax calls return a JSON object and insert that object into the DOM without manual parsing or using .innerHTML.

If used & reused correctly, this code could dramatically reduce the footprint of your web application.

Background

Over the years, I have often wanted to introduce JsonML into one of my projects, not the templating or any of that stuff, just the concept of returning a JSON object from an Ajax call, doing whatever with it and then inserting it into the document when ready. However, all the JsonML scripts I’ve come across have always felt a little bloated and not something I really wanted to introduce into my applications.

I recently found myself with a little spare time; so I decided to have a crack at writing a more compact self contained JsonML parser. It was important that it meet the following requirements:

  • Use as little code as possible
  • Be totally self contained
  • Use only w3c compliant DOM methods
  • Must not use any browser sniffing
  • Must insert nodes exactly as defined in the JsonML object
  • Must allow me to create objects at runtime using less code
  • Must allow me to specify a parent element of which to insert the new nodes
  • Must allow me to specify a function to be called as each node is created

As JsonML is defined using an array, I built my method as a prototype of the native Array object, meaning all array elements inherit the new method.

I also decided to name my method toDomNodes rather than use any JsonML reference. This is because unlike traditional JsonML parsers, my version does not attempt to overcome browser inconsistencies by inserting missing elements such as Tbody and Thead (required by Internet Explorer). This was an important decision as I wanted the elements inserted into DOM to be an exact copy of my JSON object.

Using the Code

Here is the finished, self contained method:

/* Convert JsonML Array form into nested dom nodes */
Array.prototype.toDomNodes = function(p /* parent */, 
f /* function to call on creation of each node */)
{
	if (typeof this[0] === 'string')
	{
		// create element
		var d = document, el = d.createElement(this[0]);

		// resolve parent
		p = (typeof p === 'string') ? d.getElementById(p) : p;

		// a list of attributes to map or 'set directly'
		var am = {id:"id","class":"className",rowspan:"rowSpan",
		colspan:"colSpan",cellpadding:"cellPadding",cellspacing:
		"cellSpacing",maxlength:"maxLength",readonly:"readOnly",
		contenteditable:"contentEditable"};

		// add attributes
		if (this[1] && this[1].constructor == Object) 
		{
			var n = this[1];
			
			for (var a in this[1])
			{
				if (typeof this[a] != 'function')
				{
					if (a === "style" && typeof el.style.cssText 
					!= "undefined") { el.style.cssText = n[a]; }
					else if (a === "style") { el.style = n[a]; }
					else if (am[a]) { el[am[a]] = n[a]; }
					else if (typeof this[a] != "string") { el[a] = 
					n[a]; } // not a string, set directly (expando)
					else { el.setAttribute(a, n[a]); alert(a); }
				}
			}
		}

		// insert element (must be inserted before function call, 
		// otherwise .parentNode does not exist)
		if (p) { p.appendChild(el); }
		
		// pass each node to a function (attributes will exist, 
		// but not innerText||children. can be used for adding events, etc.)
		if (typeof f === 'function') { f.apply(el); }
		
		for (var i=1,l=this.length;i<l;i++) 
		{
			var n = this[i], c = n.constructor;

			if (c === String) // add text node
			{
				el.appendChild(d.createTextNode(n));
			}
			else if (c === Array) // add children
			{
				n.toDomNodes(el, f);
			}
		}

		return el;
	}

	return null;
};

This version includes a few comments to help you understand how it works and requires 1.73k of bandwidth to use. The minified version (no comments or formatting) is only 936 bytes. I don’t know about you but I’m much happier introducing 1k of script into my projects, especially when it allows me to reduce code in other areas of the application such as replacing:

var myDiv = document.createElement("div")
myDiv.id = "myDiv"
myDiv.style.border = "1px solid red";
myDiv.style.backgroudColor = "#cccccc";
myDiv.innerHTML = "Hello World!";
document.body.appendChild(myDiv);

with code like this:

["div",{"style":"background-color:#ccc; border: 1px solid red;"},
	"Hello World!"].toDomNodes(document.body);

It also means that I can now return JsonML from my Ajax calls. As an example, imagine jsonMl contains my Ajax response:

var jsonMl =	["table",{"class":"MyTable","style":"background-color:yellow"},
		["tbody",
			["tr",
				["td",{"class":"MyTD","style":"border:1px solid black"},
				"#550758"],
				["td",{"class":"MyTD","style":"background-color:red"},
				"Example text here"]
			],
			["tr", 
				["td",{"class":"MyTD","style":"border:1px solid black"},
				"#993101"],
				["td",{"class":"MyTD","style":"background-color:green"},
				"127624015"]
			],
			["tr", 
				["td",{"class":"MyTD","style":"border:1px solid black"},
				"#E33D87"],
				["td",{"class":"MyTD","style":"background-color:blue"},
				"\u00A0",
					["span",{"id":"mySpan","style":
					"background-color:maroon;color:#fff 
					!important"},"\u00A9"],
					"\u00A0"
				]
			]
		]
	];

I can insert this object straight into the document by calling:

jsonMl.toDomNodes(document.body);

Or, I can ask the parser to tell me each time a new element is created. This allows me to apply some client side logic or assign events based on attributes, etc.

jsonMl.toDomNodes(document.body, function(){

	// this holds the new element
	alert(this.tagName);

});

The this object within your function will contain the newly created object. When your function is called, each new element will contain all its attributes and styles but not its children. That’s because this is a recursive function and child nodes have not been created at the point where it calls your function.

If you pass a parent element as the first parameter, the nodes are automatically inserted into that element. If you do not want to insert the elements into the DOM, simply pass null or no parameter. In either case, this method will return a reference to the newly created DOM nodes.

var myNewNode = jsonMl.toDomNodes();

When you are ready to insert the new nodes into the DOM, you can call the traditional appendChild method.

document.body.appendChild(myNewNode);

Points of Interest

This is the first incarnation. I have tested it and it works in IE5.5/6/7/8/9, Chrome, Firefox, Opera and Safari on the PC, iPad and iPhone. I have included a small zip file with an example of its usage along with the minified version. If there are any issues, please leave a comment and I’ll do my best to help out.

History

  • 26 May 2011 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

John Doherty

Chief Technology Officer
MammothWorkwear.com
United Kingdom United Kingdom

Member
Senior Web Developer/Systems Architect, One of the developers behind the 1999-2003 web based groupware application DeskVu/Convea. Presently CTO of MammothWorkwear.com, the UK's largest supplier of Dickies Workwear, Snickers Workwear and Caterpillar Workwear.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralCreating JSON array from ASP.Net WebService thru Jquery Ajax Call PinmemberF[]X0:17 1 Jun '11  
GeneralRe: Creating JSON array from ASP.Net WebService thru Jquery Ajax Call PinmemberJohn Doherty15:38 4 Jun '11  
GeneralGood but, is there any practicle use of this? Pinmemberaamironline21:24 28 May '11  
GeneralMy vote of 5 Pinmemberchaygavvara0:30 27 May '11  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120222.1 | Last Updated 26 May 2011
Article Copyright 2011 by John Doherty
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid