How to hide opened accordion item by clicking on a <summary>
? Currently i can only open an item by clicking on a <details>
(visually on <summary>
because its only visible until opened), but when clicking again to make an item hidden it does not work and i have to click an another item to close the previous one (which is what i want but i also want to be able to hide currently opened item by clicking on it once again).
There is an exception when i click on opened accordion item's description (<p>
) and it gets closed. But i want this closing behaviour on a <summary>
.
I want to achieve something like this: https://codepen.io/joekolade/pen/RwymroQ
- button toggling opens and hides element
- opening next accordion item closes previous one - done
const details = document.querySelectorAll("details");
details.forEach((accordion) => {
accordion.addEventListener("click", function () {
details.forEach((elem) => {
elem.removeAttribute("open");
elem.querySelector("span").classList.remove("active");
});
this.querySelector("span").classList.add("active");
});
});
summary{
list-style: none
}
details{
padding: 1rem;
background-color: lightblue;
}
details summary{
display: flex;
justify-content: space-between;
}
details:nth-child(even){
background-color: lightgreen;
}
p{
padding-top: 0.3rem;
}
.active{
transform: rotate(180deg);
}
span{
display: block;
}
<div>
<details>
<summary>
<h3>item1</h3>
<span>arrow</span>
</summary>
<p>desc1</p>
</details>
<details>
<summary>
<h3>item2</h3>
<span>arrow</span>
</summary>
<p>desc2</p>
</details>
<details>
<summary>
<h3>item3</h3>
<span>arrow</span>
</summary>
<p>desc3</p>
</details>
</div>
CodePudding user response:
I think it will be better to write custom accordion - a lot of examples in google or ready-made solutions. But if you want use <details />
I made a sketch based on your code, guess this's the behavior you expect:
const details = document.querySelectorAll("details");
let prevAccordion = null;
let prevSpan = null;
const isOpen = (accordion) =>
typeof accordion.getAttribute("open") === "object";
const closeAccordion = (accordion, span) => {
accordion.removeAttribute("open");
span.classList.remove("active");
};
details.forEach((accordion) => {
accordion.addEventListener("click", (e) => {
if (prevAccordion && prevSpan && prevAccordion !== accordion) {
closeAccordion(prevAccordion, prevSpan);
}
const span = accordion.querySelector("span");
span.classList.toggle("active");
if (isOpen(accordion)) {
prevAccordion = accordion;
prevSpan = span;
}
if (e.target.tagName === "P") closeAccordion(accordion, span);
});
});
summary {
list-style: none;
}
details {
padding: 1rem;
background-color: lightblue;
}
details summary {
display: flex;
justify-content: space-between;
}
details:nth-child(even) {
background-color: lightgreen;
}
p {
padding-top: 0.3rem;
}
.active {
transform: rotate(180deg);
}
span {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div>
<details>
<summary>
<h3>item1</h3>
<span>arrow</span>
</summary>
<p>desc1</p>
</details>
<details>
<summary>
<h3>item2</h3>
<span>arrow</span>
</summary>
<p>desc2</p>
</details>
<details>
<summary>
<h3>item3</h3>
<span>arrow</span>
</summary>
<p>desc3</p>
</details>
</div>
<script src="script.js"></script>
</body>
</html>
CodePudding user response:
You need to filter current and toggle current state and remove active from all other element classes.
This is your solution:
const details = document.querySelectorAll("details");
details.forEach((accordion) => {
accordion.addEventListener("click", function (e) {
let current = e.currentTarget;
let others = Array.from(details).filter((item) => current != item);
others.forEach((ot) => {
ot.removeAttribute("open");
ot.querySelector("span").classList.remove("active");
});
current.querySelector("span").classList.toggle("active");
});
});
summary{
list-style: none
}
details{
padding: 1rem;
background-color: lightblue;
}
details summary{
display: flex;
justify-content: space-between;
}
details:nth-child(even){
background-color: lightgreen;
}
p{
padding-top: 0.3rem;
}
.active{
transform: rotate(180deg);
}
span{
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<details>
<summary>
<h3>item1</h3>
<span>arrow</span>
</summary>
<p>desc1</p>
</details>
<details>
<summary>
<h3>item2</h3>
<span>arrow</span>
</summary>
<p>desc2</p>
</details>
<details>
<summary>
<h3>item3</h3>
<span>arrow</span>
</summary>
<p>desc3</p>
</details>
</div>