【问题标题】:Add level classes to nested list with Javascript Vanilla使用 Javascript Vanilla 将级别类添加到嵌套列表
【发布时间】:2020-04-15 16:00:48
【问题描述】:

我正在尝试将级别分类添加到具有嵌套列表项的列表中。 我想要以下结构。

<ul class="parent parent-level-1">
  <li class="child child-level-1"><a>Title level 1</a></li>
  <li class="child child-level-1"><a>Title level 1</a>
    <ul class="parent parent-level-2">
      <li class="child child-level-2"><a>Title level 2</a>
        <ul class="parent parent-level-3">
          <li class="child child-level-3"><a>Title level 3</a></li>
          <li class="child child-level-3"><a>Title level 3</a></li>
        </ul>
      </li>
      <li class="child child-level-2"><a>Title level 2</a>
        <ul class="parent parent-level-3">
          <li class="child child-level-3"><a>Title level 3</a></li>
          <li class="child child-level-3"><a>Title level 3</a></li>
          <li class="child child-level-3"><a>Title level 3</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li class="child child-level-1"><a>Title level 1</a>
    <ul class="parent parent-level-2">
      <li class="child child-level-2"><a>Title level 2</a></li>
    </ul>
  </li>
  <li class="child child-level-1"><a>Title level 1</a></li>
  <li class="child child-level-1"><a>Title level 1</a>
    <ul class="parent parent-level-2">
      <li class="child child-level-2"><a>Title level 2</a></li>
    </ul>
  </li>
</ul>

如您所见,父列表中存在一些嵌套列表,它们同时可能包含另一个嵌套列表。 我尝试了以下 Javascript 函数,其中 parent 是主列表的父级,而 level 是第一级的整数,可以是 0 或 1。

function addLevelClass(parent, level) {
  const iteration = parent.querySelectorAll("ul");

  const firstUL = parent.querySelector("ul");
  firstUL.classList.add("parent", "parent-level-" + level);

  const firstItems = parent.querySelectorAll("li");

  console.info(firstItems);

  // No point if no list items.
  if (!firstItems.length) {
    return;
  }

  //loop trough fisrt-level li
  for (let c = 0; c < firstItems.length; c++) {
    const childrenLi = firstItems[c];

    //add a child class to the li
    childrenLi.classList.add("child", "child-level-" + level);
  }

  // No point if no list items.
  if (!iteration.length) {
    return;
  }
  for (let g = 0; g < iteration.length; g++) {
    const levelIncrement = level + g;

    const parentUL = iteration[g].querySelector("ul");

    //add a parent class to the ul
    parentUL.classList.add(
      "parent",
      "parent-level-" + (level + levelIncrement)
    );

    //fetch all the li's that are direct children of the ul
    const childItems = parentUL.querySelectorAll("li");

    // No point if no list items.
    if (!childItems.length) {
      return;
    }

    //loop trough each li
    for (let c = 0; c < childItems.length; c++) {
      const childrenLi = childItems[c];

      //add a child class to the li
      childrenLi.classList.add(
        "child",
        "child-level-" + (level + levelIncrement)
      );
    }
  }
}

函数在 3er 级别的某处丢失,无法为列表的其余部分设置级别。

【问题讨论】:

  • 你正在尝试做一个 DFS 顺便说一句 -

标签: javascript ecmascript-2016


【解决方案1】:

理解这个问题的最好方法是 HTML 结构是一个 Tree 数据结构(实际上它是一个 DAG,如果你想进一步研究的话)。

这些问题是数据结构 CS101 的一部分(以及如何遍历/搜索)。提供的解决方案是 DFS。

但是,要解决此问题,您需要考虑recursion

即给定如下的树形结构

| root
|
|- leaf node
|- node with children
 |- leaf node

所以想法是首先找到根节点。 你在这里找document.querySelector('ul')

然后您将在root ul 中查找叶节点。 在这里,您将执行类似document.querySelector('ul &gt; li') 的操作。这为您提供了根中现有的子节点。

从那里开始 - 您需要将父级与子级切换以递归地执行操作。

我将在下面提供一个示例:

// helper fn
const findChildren = el => [...el.children].filter(el => el.tagName === "LI")

const root = document.querySelector("ul");
const children = findChildren(root);


const traverse = (baseNode, children, iter = 1) => {
  // process baseNode here with iter here
  baseNode.classList.add(`list-${iter.toString()}`)

  for (child of children) {
     child.classList.add(`child-${iter.toString()}`)
     let hasGranChild = [...child.children].filter(el => el.tagName === "UL").length

     if (hasGranChild) {
       let newChild = [...child.children].find(el => el.tagName === "UL");
       const granChildren = findChildren(newChild)

       traverse(newChild, granChildren, iter + 1)
     }
  }
}

traverse(root, children)
<ul>
  <li><a>Title level 1</a></li>
  <li><a>Title level 1</a>
    <ul>
      <li><a>Title level 2</a>
        <ul>
          <li><a>Title level 3</a></li>
          <li><a>Title level 3</a></li>
        </ul>
      </li>
      <li><a>Title level 2</a>
        <ul>
          <li><a>Title level 3</a></li>
          <li><a>Title level 3</a></li>
          <li><a>Title level 3</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a>Title level 1</a>
    <ul>
      <li><a>Title level 2</a></li>
    </ul>
  </li>
  <li><a>Title level 1</a></li>
  <li><a>Title level 1</a>
    <ul>
      <li><a>Title level 2</a></li>
    </ul>
  </li>
</ul>

结果

<ul class="list-1">
  <li class="child-1"><a>Title level 1</a></li>
  <li class="child-1"><a>Title level 1</a>
    <ul class="list-2">
      <li class="child-2"><a>Title level 2</a>
        <ul class="list-3">
          <li class="child-3"><a>Title level 3</a></li>
          <li class="child-3"><a>Title level 3</a></li>
        </ul>
      </li>
      <li class="child-2"><a>Title level 2</a>
        <ul class="list-3">
          <li class="child-3"><a>Title level 3</a></li>
          <li class="child-3"><a>Title level 3</a></li>
          <li class="child-3"><a>Title level 3</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li class="child-1"><a>Title level 1</a>
    <ul class="list-2">
      <li class="child-2"><a>Title level 2</a></li>
    </ul>
  </li>
  <li class="child-1"><a>Title level 1</a></li>
  <li class="child-1"><a>Title level 1</a>
    <ul class="list-2">
      <li class="child-2"><a>Title level 2</a></li>
    </ul>
  </li>
</ul>

在函数的递归性质内,您可以在函数参数中传入当前“级别”。 通过设置起始iter = 1,您不必关心树有多“深”。 当您使用 CSS 选择器直接子代(即el1 &gt; el2)时,您会问,您能给我el1 which are el2 的直接子代吗?

【讨论】:

  • const children = parent.querySelectorAll('ul > li');获取树的所有 li 项。
  • 无法完成这项工作。请问,你能解释一下辅助函数是如何工作的吗?
  • 为方便起见,我将其添加为 jsfiddle
  • @BorjaSobas 这解决了您的问题 - 请在您满意时投票并接受,谢谢。
猜你喜欢
  • 1970-01-01
  • 2019-07-23
  • 2016-06-21
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 2017-12-07
  • 2015-06-23
  • 2023-01-12
相关资源
最近更新 更多