首页 > 解决方案 > 仅获取数组的一个元素及其子元素

问题描述

我有一个多维数组categories,我正在用menuToElement函数循环这个数组。

它接受 alarray及其子元素的元素并在 a 中返回它ul。代码完全可以工作,但我不知道如何只循环数组的一个元素及其所有子元素。

例如,我想要做的是仅循环引擎元素及其所有子元素和所有子数组。我的问题是:这是否可能与我正在使用的for...of循环一起使用,因为我尝试循环遍历类别的索引并且它说它是不可迭代的。

function menuToElement(menu) {
  const ul = document.createElement("ul");
  for (const item of menu) {
    const li = document.createElement("li");
    if (Object(item) === item) {
      li.textContent = item.text + ' \u25BD';
      li.appendChild(menuToElement(item.children));
    } else {
      li.textContent = item;
    }
    ul.appendChild(li);
  }
  return ul;
}

var categories = [{
  text: "engine",
  children: [1, 2, 3, {
    text: "piston",
    children: [4, 5, 6, {
      text: "piston",
      children: [4, 5, 6]
    }]
  }]
}, {
  text: "tire",
  children: [7, 8, 9]
}];
const ul = menuToElement(categories);
document.getElementById("menu").appendChild(ul);
li>ul {
  display: none;
}

li:hover>ul {
  display: block;
}
<div id="menu"></div>

标签: javascripthtmlarraysmultidimensional-array

解决方案


首先,我们从一些让我们的生活更轻松的助手开始:elemtext-

const elem = (tag, ...children) =>
{ const node = document.createElement(tag)
  children.forEach(node.appendChild.bind(node))
  return node
}

const text =
  document.createTextNode.bind(document)

const doc =
  elem
    ( "article"                    // <article>
    , elem("p", text("wash hand")) //   <p>wash hand</p>
    , elem("p", text("read book")) //   <p>read book</p>
    , elem("p", text("stay safe")) //   <p>stay safe</p>
    )                              // </article>

document.body.appendChild(doc)
article { border: 1px solid tomato; }
p { border: 1px solid dodgerblue; }

接下来我们定义如何将一个菜单项转换为一个菜单节点,toMenu1-

  1. 如果输入item是一个对象,返回一个LIwith并item.text调用toMenuitem.children
  2. 否则(通过归纳)输入不是item对象。在这种情况下,只需返回一个纯文本LI
const toMenu1 = (item = {}) =>
  Object(item) === item
    ? elem('li', text(`${item.text} \u25BD`), toMenu(item.children)) // 1
    : elem('li', text(item))                                         // 2

我们还没有写toMenu,但在上面我们看到它需要一个项目数组。编写这个函数比上一个更容易 -

  1. 返回UL每个项目使用转换的位置toMenu1
const toMenu = (all = []) =>
  elem('ul', ...all.map(toMenu1)) // 1

在我们继续之前,展开下面的代码片段以在您自己的浏览器中验证结果 -

const elem = (tag, ...children) =>
{ const node = document.createElement(tag)
  children.forEach(node.appendChild.bind(node))
  return node
}

const text =
  document.createTextNode.bind(document)

const toMenu1 = (item = {}) =>
  Object(item) === item
    ? elem('li', text(`${item.text} \u25BD`), toMenu(item.children))
    : elem('li', text(item))
    
const toMenu = (all = []) =>
  elem('ul', ...all.map(toMenu1))

var categories = [{
  text: "engine",
  children: [1, 2, 3, {
    text: "piston",
    children: [4, 5, 6, {
      text: "piston",
      children: [4, 5, 6]
    }]
  }]
}, {
  text: "tire",
  children: [7, 8, 9]
}];

const ul = toMenu(categories)
document.getElementById("menu").appendChild(ul)
li>ul {
  display: none;
}

li:hover>ul {
  display: block;
}
<div id="menu"></div>


现在回答您的具体问题:我们可以仅使用一个输入项来构建递归菜单吗?

您可以通过以下两种方式之一执行此操作。1)通过抓住你关心的项目 -

const ul = toMenu([ categories[0] ]) // just the first category
// <ul>
//   <li>engine<ul>...</ul></li>
// </ul>

或 2) 通过使用Array.prototype.filter-

const ul = toMenu(categories.filter(c => c.text === "engine"))
// <ul>
//   <li>engine<ul>...</ul></li>
// </ul>

奖励:toMenu1和之间的特殊关系toMenu称为相互递归toMenu1调用toMenu,调用toMenu1,调用toMenu,等等......


推荐阅读