使用 Javascript Vanilla 将级别 类 添加到嵌套列表
Add level classes to nested list with Javascript Vanilla
我正在尝试将级别分类添加到具有嵌套列表项的列表中。
我想要下面的结构。
<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 级别的某处丢失,无法为列表的其余部分设置级别。
理解这个问题的最好方法是HTML结构是一个树数据结构(实际上它是一个DAG,如果你想进一步研究的话)。
这些问题是数据结构 CS101 的一部分(以及如何 traverse/search)。提供的解决方案是DFS。
但是,要解决此问题,您需要考虑 recursion
。
即给定以下树结构
| root
|
|- leaf node
|- node with children
|- leaf node
所以这个想法是你首先找到根节点的地方。
您在这里寻找 document.querySelector('ul')
然后您将在 root ul
中查找叶节点。
在这里你会做类似 document.querySelector('ul > 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>
在函数的递归性质中,可以在函数参数中传入当前的"level"。
通过设置起始 iter = 1
,您无需关心 "deep" 树的走向。
当您使用 CSS 选择器直接子项(即 el1 > el2
)时,您问的是,您能给我 el1 which are el2
的直接子项吗?
我正在尝试将级别分类添加到具有嵌套列表项的列表中。 我想要下面的结构。
<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 级别的某处丢失,无法为列表的其余部分设置级别。
理解这个问题的最好方法是HTML结构是一个树数据结构(实际上它是一个DAG,如果你想进一步研究的话)。
这些问题是数据结构 CS101 的一部分(以及如何 traverse/search)。提供的解决方案是DFS。
但是,要解决此问题,您需要考虑 recursion
。
即给定以下树结构
| root
|
|- leaf node
|- node with children
|- leaf node
所以这个想法是你首先找到根节点的地方。
您在这里寻找 document.querySelector('ul')
然后您将在 root ul
中查找叶节点。
在这里你会做类似 document.querySelector('ul > 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>
在函数的递归性质中,可以在函数参数中传入当前的"level"。
通过设置起始 iter = 1
,您无需关心 "deep" 树的走向。
当您使用 CSS 选择器直接子项(即 el1 > el2
)时,您问的是,您能给我 el1 which are el2
的直接子项吗?