I've written a function to create an HTML tree from JSON.
async function traverseTree (list, node) {
const li = document.createElement('li')
if (node.kids) {
const
ul = document.createElement('ul'),
span = document.createElement('span')
span.innerText = node.name
li.appendChild(span)
li.appendChild(ul)
list.appendChild(li)
for (const kid of node.kids) {
traverse(ul, kid)
}
return list
}
li.innerText = node.name
list.appendChild(li)
return list
}
;(async function(){
const
tree = await readJson('./tree.json'),
list = document.createElement('ul'),
output = await traverseTree(list, tree)
console.log(output)
})()
The issue is that I don't want to display the root node in the HTML tree since it is already displayed elsewhere, therefore I modified the code to exclude it.
async function traverseTree (list, node, isNotRoot) { // additional parameter
const li = document.createElement('li')
if (node.kids) {
let ul = list // now let
if (isNotRoot) { // additional conditional
const span = document.createElement('span')
ul = document.createElement('ul') // additional assignment of value
span.innerText = node.name
li.appendChild(span)
li.appendChild(ul)
list.appendChild(li)
}
for (const kid of node.kids) {
traverseTree(ul, kid, true) // additional argument
}
return list
}
li.innerText = node.name
list.appendChild(li)
return list
}
I'm wondering if there is a better, cleaner way to go about this?
1 Answer 1
Let's separate it by responsibilities into renderKids, renderName, traverseTree so that:
- the main function can start explicitly from where you want
- functions become immutable, free of side-effects
- less repeated code
- a single
return
is used - hopefully, the intent and flow become more obvious
function renderTree(json) {
function renderKids(kids) {
const ul = document.createElement('ul');
for (const kid of kids) {
ul.appendChild(traverseTree(kid))
}
return ul;
}
function renderName(name) {
const span = document.createElement('span');
span.textContent = name;
return span;
}
function traverseTree(node) {
const li = document.createElement('li');
if (node.kids) {
li.appendChild(renderName(node.name));
li.appendChild(renderKids(node.kids));
} else {
li.textContent = node.name;
}
return li;
}
return renderKids(json.kids || []);
}
Usage: const output = renderTree(await readJson('./tree.json'))
Note, traverseTree
is not asynchronous so there's no need for async
keyword.
-
2\$\begingroup\$ Modularizing code makes everything better. \$\endgroup\$SpeedOfRound– SpeedOfRound2019年11月08日 20:58:19 +00:00Commented Nov 8, 2019 at 20:58
-
\$\begingroup\$ why not make the function asynchronous? \$\endgroup\$oldboy– oldboy2019年11月12日 22:37:38 +00:00Commented Nov 12, 2019 at 22:37
-
\$\begingroup\$ man, although your code is cleaner, its wayyy harder to follow the logic, partly due to the fact that the names youve chosen for your nested functions are misnomers in certain contexts \$\endgroup\$oldboy– oldboy2019年11月12日 22:49:21 +00:00Commented Nov 12, 2019 at 22:49
-
\$\begingroup\$ and theres issues using your code because it treats the top level folders no differently than children folders \$\endgroup\$oldboy– oldboy2019年11月12日 22:57:25 +00:00Commented Nov 12, 2019 at 22:57
-
\$\begingroup\$ I can't agree. The code became so simple compared to the original spaghetti with side effects that "way harder" is an obvious exaggeration. The rest of your comments feels like a needless nitpicking and exaggeration as well. As for the "it treats the top level folders no differently", even without seeing the actual data I can't agree as it doesn't render the node name of the root which in my understanding was the entire point of the question. Anyway, this code was an example. \$\endgroup\$woxxom– woxxom2019年11月13日 04:14:51 +00:00Commented Nov 13, 2019 at 4:14
Explore related questions
See similar questions with these tags.