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, {}));
1 Answer 1
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 likedomTree
(since it's a representation of the DOM) or something.doc
is vague. Why not name it asdocument
straight away. If it isdocument
then it will just be shadowing thedocument
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.
-
\$\begingroup\$ Thanks so much for this feedback! I'll writ an example that uses the third argument. :) \$\endgroup\$Noitidart– Noitidart2015年07月10日 15:02:26 +00:00Commented Jul 10, 2015 at 15:02