0

I have the following code below and trying to find a way to generate <ul> and <li> to output an unordered list via Javascript.

Question(s):

  1. How can I parse the following array to generate a nested unordered list html?
  2. If I were to use recursion, how would I recursively iterate through all nested children to print a nested unordered list?

What I tried that didn't work

const data = [
    {
        name: '125313-7j',
    },
    {
        name: '584996-s7',
        _children: [
            {
                name: '747773-jw',
            },
            {
                name: '377526-zg',
                _children: [
                    {
                        name: '955330-wp',
                    },
                    {
                        name: '343693-9x',
                    },
                ],
            },
            {
                name: '638483-y2'
            },
        ],
    },
    {
        name: '306979-mx'
    },
    {
        name: '066195-o1',
        _children: [],
    },
];

$(function() {
let innerHtml = '<ul>';
data.forEach( item => {
  innerHTML += '<li>' + '<div>' + item.name + '</div>';
    if(item._children) {
      // ***ISSUE***  this doesn't print all the children so i cannot recursively get all the children
       console.log('children', item._children);
    }
});

innerHtml += '</ul>';
});

Expected output

<ul>
  <li> <div>Name1</div>
     <ul>
        <li><div>Name2</div>
         .... continue the next unordered list until all children have been displayed ...
      </ul>
   </li>
</ul>
0

3 Answers 3

2

As you seem to be using jQuery, here is a solution using jQuery style, producing the object structure, ready to inject in the HTML document:

function createHtml(data) {
    return $("<ul>").append(
        (data ?? []).map(({ name, _children }) =>
            $("<li>").append(
                $("<div>").text(name),
                data?.length ? createHtml(_children) : []
            )
        )
    );
}

const data = [{name: '125313-7j',},{name: '584996-s7',_children: [{name: '747773-jw',},{name: '377526-zg',_children: [{name: '955330-wp',},{name: '343693-9x',},],},{name: '638483-y2'},],},{name: '306979-mx'},{name: '066195-o1',_children: [],},];
const $ul = createHtml(data);
$("#container").append($ul);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

<div id="container"></div>

An advantage of setting the text with text(), is that you'll not have issues if the item's name has character sequences that could be misinterpreted as HTML, such as < or & in combination with other characters.

Sign up to request clarification or add additional context in comments.

Comments

1

For your requirement, the recursive solution seems more intuitive to me. Let me share my insights for approaching this:

# Pseudocode

# generateList(items)
#   1. init: html<String> with opening "<ul>" tag. 
#            Can also use array. See References section

#   2. for item in items
#       2.1. html += "<li><div>", item.name, "</div>"
#       2.2. if item has _children and the length of _children is greater than 0
#            2.2.1. html += result of calling generateNestedList recursively with item._children
#       2.3. html += closing "<\li>"

#   3. html += closing "</ul>"

#   4. return html.
# end function

const data = [{
    name: '125313-7j',
  },
  {
    name: '584996-s7',
    _children: [{
        name: '747773-jw',
      },
      {
        name: '377526-zg',
        _children: [{
            name: '955330-wp',
          },
          {
            name: '343693-9x',
          },
        ],
      },
      {
        name: '638483-y2'
      },
    ],
  },
  {
    name: '306979-mx'
  },
  {
    name: '066195-o1',
    _children: [],
  },
];

function generateList(items) {
  let html = '<ul>';

  items.forEach((item) => {
    html += '<li><div>' + item.name + '</div>';
    if (item._children?.length > 0) {
      html += generateList(item._children);
    }
    html += '</li>';
  });

  html += '</ul>';
  return html;
}

const innerHtml = generateList(data);
// console.log(innerHtml);
document.getElementById("list-container").innerHTML = innerHtml;
<div id="list-container"></div>


References:

  1. Array Join vs String Concat
  2. Optional Chaining(?.)

Comments

0

I'm not quite sure what you mean by your two questions. Below is a version which converts your object structure into the sort of nested list structure you're looking for.

Your code will not work without one big change. You need to make a function out of the body and call that function in your *** ISSUE *** block.

My version uses Array.prototype.map instead of forEach and string interpolation instead of concatenation, but expresses much the same logic. Note the separation of concerns. toList creates an HTML string. It's left to the calling code to insert that into the document.

const toList = (xs) => `<ul>${xs.map (x => `<li><div>${x.name}</div>${
  '_children' in x && x._children.length > 0 ? toList(x._children) : ``
}</li>`).join('')}</ul>`

const data = [{name: "125313-7j"}, {name: "584996-s7", _children: [{name: "747773-jw"}, {name: "377526-zg", _children: [{name: "955330-wp"}, {name: "343693-9x"}]}, {name: "638483-y2"}]}, {name: "306979-mx"}, {name: "066195-o1", _children: []}]

const html = toList(data)
console.log(html)
document.getElementById('content').innerHTML = html
<div id="content" />

We don't try to format the HTML nicely with line breaks and indentation. We could add that, but for HTML it's usually an unnecessary complexity. However, if we were trying to create a nested, indented plain text list, we might add a depth (here d) parameter, which we increment on each recursive level, and use to set indentation level:

const toText = (xs, d = 0) => xs.map (x => '    '.repeat(d) + x.name +
  ('_children' in x && x._children.length > 0 ? '\n' + toText(x._children, d + 1) : '')
).join('\n')

const data = [{name: "125313-7j"}, {name: "584996-s7", _children: [{name: "747773-jw"}, {name: "377526-zg", _children: [{name: "955330-wp"}, {name: "343693-9x"}]}, {name: "638483-y2"}]}, {name: "306979-mx"}, {name: "066195-o1", _children: []}]

console.log(toText(data))

Comments

Your Answer

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.