1
\$\begingroup\$

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?

asked Nov 8, 2019 at 0:05
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Let's separate it by responsibilities into renderKids, renderName, traverseTree so that:

  1. the main function can start explicitly from where you want
  2. functions become immutable, free of side-effects
  3. less repeated code
  4. a single return is used
  5. 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.

answered Nov 8, 2019 at 16:35
\$\endgroup\$
19
  • 2
    \$\begingroup\$ Modularizing code makes everything better. \$\endgroup\$ Commented Nov 8, 2019 at 20:58
  • \$\begingroup\$ why not make the function asynchronous? \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Nov 13, 2019 at 4:14

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.