一次只允许打开一个的手风琴
Accordion that allows only one open at a time
我有一个手风琴非常好用,它在网站上看起来不错并且可以正常工作。但是,我正在尝试向它添加更多 JavaScript 功能,使其看起来更专业。
目前,手风琴允许您同时打开多个面板,即如果我打开一个选项卡,然后打开另一个选项卡,两个选项卡将同时打开。关闭这些面板的唯一方法是 re-click 在 header.
我想要一些 JavaScript 代码来防止同时打开多个选项卡,因此如果我单击一个新面板,它应该首先关闭现有的打开面板。这是我的 HTML 手风琴代码:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
<div class="accordion"><b>Heading 1</b></div>
<div class="panel">
<p class="text-light">Text 1</p>
</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel">
<p class="text-light">Text 2</p>
</div>
- 为每个手风琴动态添加数据 ID div(基于 0 个数据 ID)
- 单击时 -> 如果索引与该数据 ID 匹配 -> 打开(显示)否则 (display:none)
- 我还要添加一个 isActive 标志变量:let currentOpenID = cur;
这样一来,如果您单击已经打开的手风琴元素,您 display:none 也会...
逻辑是:你需要一些东西来比较,那就是你的开放标志,否则关闭。
我也会用
panel.style.display: none;
panel.style.display: block;
您需要做的就是添加一个跟踪当前活动元素的变量。每次单击时,您的代码会将当前活动项目的最大高度更改为 0。一旦这样做,它将设置刚刚单击的项目的最大高度,并将该项目设置为当前活动的项目。
我稍微重写了您的代码,但这绝对适合您。
var acc = document.querySelectorAll(".accordion");
var active = null;
acc.forEach((item, i) => {
item.addEventListener("click", function () {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if(active) {
active.style.maxHeight = null;
}
if(panel !== active) {
panel.style.maxHeight = panel.scrollHeight + "px";
active = panel
} else {
active = null
}
});
});
.panel {
max-height: 0;
overflow: hidden;
}
<div class="accordion"><b>Heading 1</b></div>
<div class="panel">
<p class="text-light">Text 1</p>
</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel">
<p class="text-light">Text 2</p>
</div>
<div class="accordion"><b>Heading 3</b></div>
<div class="panel">
<p class="text-light">Text 3</p>
</div>
<div class="accordion"><b>Heading 4</b></div>
<div class="panel">
<p class="text-light">Text 4</p>
</div>
每次单击标题时,您不仅需要切换所单击项目的状态,还需要遍历其他手风琴项目并关闭它们。在下面的代码中,我稍微简化了您的示例并使用 display: none;
隐藏手风琴项:
const accordions = Array.from(document.getElementsByClassName("accordion"));
accordions.forEach(accordion1 =>
accordion1.addEventListener("click", () =>
accordions.forEach(accordion2 =>
accordion2.nextElementSibling.classList.toggle(
"hidden",
accordion1 !== accordion2 ||
!accordion1.nextElementSibling.classList.contains("hidden")
)
)
)
);
.panel.hidden {
display: none;
}
<div class="accordion"><b>Heading 1</b></div>
<div class="panel hidden">Panel 1</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel hidden">Panel 2</div>
<div class="accordion"><b>Heading 3</b></div>
<div class="panel hidden">Panel 3</div>
我最近确实学到了一些东西...当您需要在具有相同父节点的多个节点上侦听事件时,最佳实践之一是使用事件委托,也就是说,侦听用于点击父节点。
那么您可能需要与
中包含的其他链接进行交互。为了处理这个问题,closest 方法将帮助您 selecting 一个共同的父节点(参见 https://developer.mozilla.org/en-US/docs/Web/API/Element/closest ),然后 select 通过使用 querySelectorAll(或者 getElementsByClassName,如果你愿意的话)节点
您仍然需要从这里管理您的动画,但我认为它会对您有所帮助。
// DOM here
let nav = document.querySelector(".nav");
// Handlers here
const clickHandler = function (e) {
if (e.target.classList.contains("nav__link")) {
const link = e.target;
const siblings = link.closest(".nav").querySelectorAll(".nav__link");
link.classList.toggle("active");
// removes all actives except for the clicked one
siblings.forEach((el) => {
if (el !== link) el.classList.remove("active");
});
}
};
// Listeners here
nav.addEventListener("click", clickHandler);
body {
font-family: sans-serif;
}
.nav__link {
display: block;
width: 100%;
}
.active {
background: #0f0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div class="nav">
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Section 1</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--2">Section 2</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--3">Section 3</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--4"
>Section 4</a
>
</li>
</ul>
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
如果你想更进一步,它看起来像这样:
// DOM here
let nav = document.querySelector(".nav");
// Handlers here
const clickHandler = function (e) {
if (e.target.classList.contains("nav__link")) {
const link = e.target; // clicked link
const siblings = link.closest(".nav").querySelectorAll(".nav__link");
link.classList.toggle("active");
link.children[0].classList.toggle("hidden");
// removes all actives except for the clicked one
siblings.forEach((el) => {
if (el !== link) {
el.classList.remove("active");
el.children[0].classList.add("hidden");
}
});
}
};
// Listeners here
nav.addEventListener("click", clickHandler);
body {
font-family: sans-serif;
}
.nav__link {
display: block;
width: 100%;
transition: all 0.3s;
}
.active {
background: #0f0;
}
.hidden {
display: none;
}
<div class="nav">
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Section 1
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--2">Section 2
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--3">Section 3
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--4">section 4
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
</ul>
</div>
</div>
我有一个手风琴非常好用,它在网站上看起来不错并且可以正常工作。但是,我正在尝试向它添加更多 JavaScript 功能,使其看起来更专业。
目前,手风琴允许您同时打开多个面板,即如果我打开一个选项卡,然后打开另一个选项卡,两个选项卡将同时打开。关闭这些面板的唯一方法是 re-click 在 header.
我想要一些 JavaScript 代码来防止同时打开多个选项卡,因此如果我单击一个新面板,它应该首先关闭现有的打开面板。这是我的 HTML 手风琴代码:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
<div class="accordion"><b>Heading 1</b></div>
<div class="panel">
<p class="text-light">Text 1</p>
</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel">
<p class="text-light">Text 2</p>
</div>
- 为每个手风琴动态添加数据 ID div(基于 0 个数据 ID)
- 单击时 -> 如果索引与该数据 ID 匹配 -> 打开(显示)否则 (display:none)
- 我还要添加一个 isActive 标志变量:let currentOpenID = cur; 这样一来,如果您单击已经打开的手风琴元素,您 display:none 也会...
逻辑是:你需要一些东西来比较,那就是你的开放标志,否则关闭。
我也会用
panel.style.display: none;
panel.style.display: block;
您需要做的就是添加一个跟踪当前活动元素的变量。每次单击时,您的代码会将当前活动项目的最大高度更改为 0。一旦这样做,它将设置刚刚单击的项目的最大高度,并将该项目设置为当前活动的项目。
我稍微重写了您的代码,但这绝对适合您。
var acc = document.querySelectorAll(".accordion");
var active = null;
acc.forEach((item, i) => {
item.addEventListener("click", function () {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if(active) {
active.style.maxHeight = null;
}
if(panel !== active) {
panel.style.maxHeight = panel.scrollHeight + "px";
active = panel
} else {
active = null
}
});
});
.panel {
max-height: 0;
overflow: hidden;
}
<div class="accordion"><b>Heading 1</b></div>
<div class="panel">
<p class="text-light">Text 1</p>
</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel">
<p class="text-light">Text 2</p>
</div>
<div class="accordion"><b>Heading 3</b></div>
<div class="panel">
<p class="text-light">Text 3</p>
</div>
<div class="accordion"><b>Heading 4</b></div>
<div class="panel">
<p class="text-light">Text 4</p>
</div>
每次单击标题时,您不仅需要切换所单击项目的状态,还需要遍历其他手风琴项目并关闭它们。在下面的代码中,我稍微简化了您的示例并使用 display: none;
隐藏手风琴项:
const accordions = Array.from(document.getElementsByClassName("accordion"));
accordions.forEach(accordion1 =>
accordion1.addEventListener("click", () =>
accordions.forEach(accordion2 =>
accordion2.nextElementSibling.classList.toggle(
"hidden",
accordion1 !== accordion2 ||
!accordion1.nextElementSibling.classList.contains("hidden")
)
)
)
);
.panel.hidden {
display: none;
}
<div class="accordion"><b>Heading 1</b></div>
<div class="panel hidden">Panel 1</div>
<div class="accordion"><b>Heading 2</b></div>
<div class="panel hidden">Panel 2</div>
<div class="accordion"><b>Heading 3</b></div>
<div class="panel hidden">Panel 3</div>
我最近确实学到了一些东西...当您需要在具有相同父节点的多个节点上侦听事件时,最佳实践之一是使用事件委托,也就是说,侦听用于点击父节点。
那么您可能需要与
您仍然需要从这里管理您的动画,但我认为它会对您有所帮助。
// DOM here
let nav = document.querySelector(".nav");
// Handlers here
const clickHandler = function (e) {
if (e.target.classList.contains("nav__link")) {
const link = e.target;
const siblings = link.closest(".nav").querySelectorAll(".nav__link");
link.classList.toggle("active");
// removes all actives except for the clicked one
siblings.forEach((el) => {
if (el !== link) el.classList.remove("active");
});
}
};
// Listeners here
nav.addEventListener("click", clickHandler);
body {
font-family: sans-serif;
}
.nav__link {
display: block;
width: 100%;
}
.active {
background: #0f0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div class="nav">
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Section 1</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--2">Section 2</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--3">Section 3</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--4"
>Section 4</a
>
</li>
</ul>
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
如果你想更进一步,它看起来像这样:
// DOM here
let nav = document.querySelector(".nav");
// Handlers here
const clickHandler = function (e) {
if (e.target.classList.contains("nav__link")) {
const link = e.target; // clicked link
const siblings = link.closest(".nav").querySelectorAll(".nav__link");
link.classList.toggle("active");
link.children[0].classList.toggle("hidden");
// removes all actives except for the clicked one
siblings.forEach((el) => {
if (el !== link) {
el.classList.remove("active");
el.children[0].classList.add("hidden");
}
});
}
};
// Listeners here
nav.addEventListener("click", clickHandler);
body {
font-family: sans-serif;
}
.nav__link {
display: block;
width: 100%;
transition: all 0.3s;
}
.active {
background: #0f0;
}
.hidden {
display: none;
}
<div class="nav">
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Section 1
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--2">Section 2
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--3">Section 3
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--4">section 4
<ul class="hidden">
<li>list item</li>
<li>list item</li>
</ul>
</a>
</li>
</ul>
</div>
</div>