JavaScript:如何从数组创建无序列表?

6
我有以下数组,想要从中创建一个无序列表,但我在生成符合格式的无序列表方面遇到了困难。我搜索了类似的问题,但现有的解决方案都不适用于我的问题。
var myArray = ['Value 1', ['Inner value 1', 'Inner value 2', 'Inner value 3', 'Inner value 4'], 'Value 2', 'Value 3', 'Value 4', 'Value 5', 'Value 6'];

这是我的 JavaScript 代码:

function arrToUl(arr) {
  var div = document.getElementById('myList');
  var ul = document.createElement('ul');

  for (var i = 0; i < arr.length; i++) {

    if (arr[i] instanceof Array) {
      var list = arrToUl(arr[i]);
    } else {
      var li = document.createElement('li');
      li.appendChild(document.createTextNode(arr[i]));
      console.log(ul.appendChild(li));
    }
    div.appendChild(ul);
  }
}

arrToUl(myArray);

上述代码产生了以下结果:
<ul>
<li>Value 1</li>
<li>Inner Value 1</li>
<li>Inner Value 2</li>
<li>Inner Value 3</li>
<li>Inner Value 4</li>
<li>Value 2</li>
<li>Value 3</li>
<li>Value 4</li >
<li>Value 5</li >
<li>Value 6</li>

但是结果应该如下所示:
<ul>
<li>Value 1
    <ul>
        <li>Inner Value 1</li>
        <li>Inner Value 2</li>
        <li>Inner Value 3</li>
        <li>Inner Value 4</li>
    </ul>
</li>
<li>Value 2</li>
<li>Value 3</li>
<li>Value 4</li>
<li>Value 5</li>
<li>Value 6</li>

谢谢你的帮助。
感谢您的协助。

如果myArray = ['Value 1',['Inner value 1','Inner value 2','Inner value 3','Inner value 4'],['Inner value 5','Inner value 6','Inner value 7','Inner value 8'],'Value 2','Value 3','Value 4','Value 5','Value 6'],那么结果应该是什么? - Khalid T.
使用 instanceof 来检测一个数组是不可靠的,请使用 Array.isArray 代替。 - RobG
6个回答

9
你已经将所有的<ul>元素附加到myList <div>中。为了改变它,我已经向arrToUl(root, arr)函数添加了一个新参数。
这个新参数root决定了被创建的<ul>应该附加到谁身上,所以如果函数遇到子数组,它会使用先前的列表项作为子列表的根来创建子列表。

var myArray = ['Value 1', ['Inner value 1', 'Inner value 2', 'Inner value 3', 'Inner value 4'], 'Value 2', 'Value 3', 'Value 4', 'Value 5', 'Value 6'];

function arrToUl(root, arr) {
  var ul = document.createElement('ul');
  var li;
  
  root.appendChild(ul); // append the created ul to the root

  arr.forEach(function(item) {
    if (Array.isArray(item)) { // if it's an array
      arrToUl(li, item); // call arrToUl with the li as the root
      return;
    }
    
    li = document.createElement('li'); // create a new list item
    li.appendChild(document.createTextNode(item)); // append the text to the li
    ul.appendChild(li); // append the list item to the ul
  });
}

var div = document.getElementById('myList');

arrToUl(div, myArray);
<div id="myList"></div>


仅追加 append ...? - Nina Scholz
错误:"message":"对象不支持属性或方法'append'", - Nina Scholz
这类似于jQuery的ParentNode.append,但似乎支持不足。谢谢 :) - Ori Drori

1
你的函数名为arrToUl,那么它应该只做这件事:将数组转换为ul
一旦你有了ul,你可以在任何想要插入的地方插入它,但是要在函数外部进行操作。
然后一切就变得清晰明了了。

function arrToUl(arr) {
  var ul = document.createElement('ul'), li;
  for (var i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      li.appendChild(arrToUl(arr[i]));
    } else {
      li = document.createElement('li');
      li.appendChild(document.createTextNode(arr[i]));
      ul.appendChild(li);
    }
  }
  return ul;
}
var myArray = ['Value 1', ['Inner value 1', 'Inner value 2', 'Inner value 3', 'Inner value 4'], 'Value 2', 'Value 3', 'Value 4', 'Value 5', 'Value 6'];
document.body.appendChild(arrToUl(myArray));


1

With vanilla JS (using ES6 syntax):

const myArray = [
  "Value 1",
  ["Inner value 1", "Inner value 2", "Inner value 3", "Inner value 4"],
  "Value 2",
  "Value 3",
  "Value 4",
  "Value 5",
  "Value 6",
];

const arrayToUL = (arr) => {
  const ul = document.createElement("ul");
  ul.append(
    ...arr.map((value) => {
      if (Array.isArray(value)) return arrayToUL(value);
      const li = document.createElement("li");
      li.textContent = value;
      return li;
    })
  );
  return ul;
};

document.body.appendChild(arrayToUL(myArray));

在ReactJS中(以数组形式作为其子元素):

const List = ({ children }) => (
  <ul>
    {children.map((val) =>
      Array.isArray(val) ? List({ children: val }) : <li>{val}</li>
    )}
  </ul>
);

const myArray = [
  "Value 1",
  ["Inner value 1", "Inner value 2", "Inner value 3", "Inner value 4"],
  "Value 2",
  "Value 3",
  "Value 4",
  "Value 5",
  "Value 6",
];

const App = () => <List>{myArray}</List>;

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>


0
你可以使用一个针对目标DOM元素的扩展函数,保存最后一个li并将其附加到它上面。

function iter(target) {
    var ul = document.createElement('ul'),
        li;

    target.appendChild(ul);
    return function (a) {
        if (Array.isArray(a)) {
            if (!li) {
                li = document.createElement('li');
                ul.appendChild(li);
            }
            a.forEach(iter(li));
            return;
        }
        li = document.createElement('li');
        li.appendChild(document.createTextNode(a));
        ul.appendChild(li);
    };
}

var myArray = ['Value 1', ['Inner value 1', 'Inner value 2', 'Inner value 3', 'Inner value 4'], 'Value 2', 'Value 3', 'Value 4', 'Value 5', 'Value 6'];

myArray.forEach(iter(document.getElementById('myList')));
<div id="myList"></div>


感谢您的所有帮助。所有的解决方案对我都很有效。 - EBamba
@OriDrori,现在它检查存在性。 - Nina Scholz

0

你的递归方法并没有说明新子节点应该添加到哪里。也许你应该在每次获取数组实例时,给最后一个元素分配一个唯一的ID。然后你可以将这个ID作为函数参数传递。

GUID生成器是生成唯一ID的好方法。


这不是一个答案,应该是一条评论。 - RobG

0
我会提出一个基于递归的解决方案,这似乎更清晰、更不容易出错。它产生了稍微不同的结果,因为它将每个数组元素映射到同一级别的节点,我认为这应该是从数组创建 ul 的输出。
var myArray = [
  'Value 1',
  ['Inner value 1', 'Inner value 2', 'Inner value 3', 'Inner value 4'],
  'Value 2',
  'Value 3',
  'Value 4',
  'Value 5',
  'Value 6'
];

function appendItems(arr, el) {
  if(!arr.length) return el;
  else {
      var head = arr.shift();
      var child = transform(head);
      var li = document.createElement('li');
      li.appendChild(child);
      el.appendChild(li);
      return appendItems(arr, el);
  }
}

function transform(item) {
  if(Array.isArray(item)) {
    var ul = document.createElement('ul');
    return appendItems(item, ul);
  } else {
    return document.createTextNode(item);
  }
}

var ul = transform(myArray);

如果您想要所提到的输出结果,最好将输入格式更改为每个节点一次迭代。

var arr = [
  {label: 'value1', subitems: ['Inner value 1', 'Inner value 2', 'Inner value 3', 'Inner value 4']}
];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接