我正在构建一个移动菜单。它有三个级别。当您单击链接时,它会向单击的链接和下一个 ul 添加一个打开的类。我正在通过循环遍历所有子元素并组合每个元素的高度来计算 ul 的高度。这部分正在工作,我遇到的问题是当计算第二级子级时,我也需要能够重新计算父 ul,但这需要在计算子 ul 后发生,以便它获得新的高度。
测试用例:
-
单击父级 1
-
单击子项 1(不会重新计算身高(
单击 父项 1 关闭,再次单击(高度现已重新计算(
{
const primary = document.querySelector(".primary"),
secondary = document.querySelectorAll(".secondary"),
children = document.querySelectorAll(".has-children");
for (let [i, _this] of children.entries()) {
let currentUL = secondary[i];
_this.addEventListener("click", e => {
e.preventDefault();
// toggle open class
_this.classList.toggle("open");
currentUL.classList.toggle("open");
let children = currentUL.children,
total = 0;
for (let x = 0; x < children.length; x++) {
total = total + children[x].offsetHeight;
}
currentUL.classList.contains("open") ?
(currentUL.style.height = `${total}px`) :
(currentUL.style.height = 0);
});
}
}
.primary {
max-width: 500px;
margin: 0 auto;
}
.primary a {
display: block;
padding: 20px;
border-bottom: 1px solid grey;
font-size: 16px;
color: green;
}
.secondary {
height: 0;
overflow: hidden;
transition-delay: 0.3s;
transition: height 0.3s ease;
margin-left: 38px;
}
<ul class="primary">
<li>
<a href="" class="has-children">Parent 1</a>
<ul class="secondary">
<li>
<a href="" class="has-children">Child 1</a>
<ul class="secondary">
<li>
<a href="">1</a>
</li>
<li>
<a href="">2</a>
</li>
<li>
<a href="">3</a>
</li>
<li>
<a href="">4</a>
</li>
<li>
<a href="">5</a>
</li>
<li>
<a href="">6</a>
</li>
<li>
<a href="">7</a>
</li>
<li>
<a href="">8</a>
</li>
<li>
<a href="">9</a>
</li>
<li>
<a href="">10</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
问题是当用户单击文本"父 1"时,第一个ul.secondary
元素高度会正确更新,但是当用户单击"子元素 1"时,只有第二个ul.secondary
高度会正确更新,而不是第一个ul.secondary
。一个可能的解决方案是重新计算父ul
一旦子级发生变化,由于动画延迟,我在计算父高度之前使用了 0.3 秒的timeout
。
要检查我在说什么,您可以使用浏览器的开发工具,选项卡"元素",并查看每次用户交互后如何修改元素的属性style.height
。
我添加的代码是在注释// This is the code I added
之后
const primary = document.querySelector(".primary"),
secondary = document.querySelectorAll(".secondary"),
children = document.querySelectorAll(".has-children");
for (let [i, _this] of children.entries()) {
let currentUL = secondary[i];
_this.addEventListener("click", e => {
e.preventDefault();
// toggle open class
_this.classList.toggle("open");
currentUL.classList.toggle("open");
calculateULHeight(currentUL);
// This is the code I added
let parentUL = currentUL.parentElement.parentElement;
if (parentUL.classList.contains('secondary')) {
setTimeout(function() {
calculateULHeight(parentUL);
}, 300);
}
});
}
function calculateULHeight(UL) {
let children = UL.children,
total = 0;
for (let x = 0; x < children.length; x++) {
total = total + children[x].offsetHeight;
}
UL.classList.contains("open") ?
(UL.style.height = `${total}px`) :
(UL.style.height = 0);
}
.primary {
max-width: 500px;
margin: 0 auto;
}
.primary a {
display: block;
padding: 20px;
border-bottom: 1px solid grey;
font-size: 16px;
color: green;
}
.secondary {
height: 0;
overflow: hidden;
transition-delay: 0.3s;
transition: height 0.3s ease;
margin-left: 38px;
}
<ul class="primary">
<li>
<a href="" class="has-children">Parent 1</a>
<ul class="secondary">
<li>
<a href="" class="has-children">Child 1</a>
<ul class="secondary">
<li>
<a href="">1</a>
</li>
<li>
<a href="">2</a>
</li>
<li>
<a href="">3</a>
</li>
<li>
<a href="">4</a>
</li>
<li>
<a href="">5</a>
</li>
<li>
<a href="">6</a>
</li>
<li>
<a href="">7</a>
</li>
<li>
<a href="">8</a>
</li>
<li>
<a href="">9</a>
</li>
<li>
<a href="">10</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>