2
\$\begingroup\$

For the MDN documentation website we have written this jsonToDOM function and I was wondering how you all would improve it. It takes a json structure and creates DOM elements out of it and appends it to the document. It is written in such a way that the user has to pass the window/document as they might run this code from a scope which has no access to window object (Browser addons for example)

function jsonToDOM(json, doc, nodes) {
 var namespaces = {
 html: 'http://www.w3.org/1999/xhtml',
 xul: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
 };
 var defaultNamespace = namespaces.html;
 function namespace(name) {
 var m = /^(?:(.*):)?(.*)$/.exec(name); 
 return [namespaces[m[1]], m[2]];
 }
 function tag(name, attr) {
 if (Array.isArray(name)) {
 var frag = doc.createDocumentFragment();
 Array.forEach(arguments, function (arg) {
 if (!Array.isArray(arg[0]))
 frag.appendChild(tag.apply(null, arg));
 else
 arg.forEach(function (arg) {
 frag.appendChild(tag.apply(null, arg));
 });
 });
 return frag;
 }
 var args = Array.slice(arguments, 2);
 var vals = namespace(name);
 var elem = doc.createElementNS(vals[0] || defaultNamespace, vals[1]);
 for (var key in attr) {
 var val = attr[key];
 if (nodes && key == 'key')
 nodes[val] = elem;
 vals = namespace(key);
 if (typeof val == 'function')
 elem.addEventListener(key.replace(/^on/, ''), val, false);
 else
 elem.setAttributeNS(vals[0] || '', vals[1], val);
 }
 args.forEach(function(e) {
 try {
 elem.appendChild(
 Object.prototype.toString.call(e) == '[object Array]'
 ?
 tag.apply(null, e)
 :
 e instanceof doc.defaultView.Node
 ?
 e
 :
 doc.createTextNode(e)
 );
 } catch (ex) {
 elem.appendChild(doc.createTextNode(ex));
 }
 });
 return elem;
 }
 return tag.apply(null, json);
}

Here's an example of the usage:

var json =
['html:div', {style:'background-color:springgreen'},
 ['html:form', {id:'myFirstForm'},
 ['html:input', {type:'text', value:'my field'}],
 ['html:button', {id:'myBtn'}, 'Button Text Content']
 ],
 ['html:form', {id:'mySecondForm'},
 ['html:input', {type:'text', value:'my field for second form'}],
 ['html:div', {},
 'Sub Div with textcontent and siblings',
 ['html:br', {}],
 ['html:input', {type:'checkbox', id:'mycheck'}],
 ['html:label', {for:'mycheck'},
 'here is text of label, click this text will check the box'
 ]
 ]
 ]
];
document.body.appendChild(jsonToDOM(json, document, {}));
asked Jul 10, 2015 at 3:04
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
function jsonToDOM(json, doc, nodes)

I would suggest better naming for these.

  • json is a bad name since JSON is a string data transfer format based on JS literal object notation (hence the name). I suggest something like domTree (since it's a representation of the DOM) or something.

  • doc is vague. Why not name it as document straight away. If it is document then it will just be shadowing the document from the outer scope. No harm done.

  • nodes... Not sure what this does judging by the name. Your example provides a {} which isn't very clear what it does. Better name it to something better.


jsonToDom([
 // very deep tree
], document, {});
// to
jsonToDom(document, {}, [
 // very deep tree
]);

I suggest moving dom and nodes before json to allow users to create inlined calls without burying dom and nodes below the call.


var namespaces = {
 html: 'http://www.w3.org/1999/xhtml',
 xul: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
};
// to
var NAMESPACES= {
 HTML: 'http://www.w3.org/1999/xhtml',
 XUL: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
};

These appear to be "constants". Use uppercase and underscores convention to indicate constant-ness.

In addition, anything "constant" should be placed in a variable. This includes string literals (like [object Array]), patterns (like /^on/), and others.


var defaultNamespace = namespaces.html;

This appears to be useless since it's always HTML. Did you forget something?


Your ternary needs a bit of cleaning. The way I usually do it is to line up the the ? and : for visibility of the two options that come after.

condition1 ? foo
 : condition2 ? bar
 : baz;

If an if, for, while and similar operations have blocks that have more than one line, regardless if the operation is one expression, just put {} for safety. Better, just use {} for blocks all the time.

answered Jul 10, 2015 at 6:39
\$\endgroup\$
1
  • \$\begingroup\$ Thanks so much for this feedback! I'll writ an example that uses the third argument. :) \$\endgroup\$ Commented Jul 10, 2015 at 15:02

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.