I think I may have been staring at this problem for too long and missing something simple. I can create an accordion that opens and closes each tab on click. But when I tried to revise the code so that each tab closes when clicked on another automatically, the tab you click on will no longer close itself once clicked. I have shortened the code a bit and will be adjusting the aria labels later, but why won't the tab close when clicked on itself? The active class isn't being removed.
JSFiddle here: https://jsfiddle.net/huz08qts/6/
document.addEventListener("DOMContentLoaded", () => {
const toggleClasses = (a) => {
let collapser = a.querySelector(".accordion-collapser");
let panel = a.querySelector(".accordion-panel");
let title = a.querySelector(".accordion-title");
document.querySelectorAll(".active").forEach(e => e.classList.remove("active"))
if(panel.classList.contains("active")) {
panel.classList.remove("active");
} else {
panel.classList.add("active");
}
// title.classList.add("active");
}
document.querySelectorAll(".accordion").forEach((a) => {
a.addEventListener("click", () => toggleClasses(a), false)
});
})
html {
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.accordion-icon {
height: 50px;
width: 50px;
margin-left: 10px;
}
.accordion-group {
margin-bottom: 21px;
}
.accordion-heading {
padding: 0;
margin: 0;
border: 1px solid #cccccc;
border-top: 0px;
border-bottom: 0px;
position: relative;
display: flex;
align-items: center;
}
.accordion-heading:focus-within {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
outline-style: dotted;
outline-width: 2px;
}
.accordion-group .accordion-heading:first-child {
border-top: 1px solid #cccccc;
}
.accordion-group .accordion:last-child .accordion-heading {
border-bottom: 1px solid #cccccc;
}
.accordion-group .accordion:last-child .accordion-panel {
border-top: 0;
}
.accordion-title {
color: #363c3e;
line-height: 1.3em;
font-weight: 600;
font-size: 18px;
}
.accordion-heading a {
position: relative;
}
.accordion-title:hover {
color: #00a950;
cursor: pointer;
}
.accordion-heading:hover {
cursor: pointer;
}
.accordion-heading h3:after {
content: " ";
background: url("https://i.postimg.cc/13fCTwcc/collapse-icon.png");
position: absolute;
top: 50%;
right: 15px;
width: 37px;
height: 37px;
transform: translateY(-50%) rotate(0);
transition: transform 0.3s, -webkit-transform 0.3s;
}
.accordion-heading h3.active:after {
transform: translateY(-50%) rotate(-45deg);
}
.accordion-collapser {
display: inline-block;
color: #363c3e;
padding: 10px 55px 10px 15px;
text-decoration: none;
}
.accordion-collapser:focus {
outline: 0px;
}
.accordion-collapser:hover {
color: #00a950;
text-decoration: none;
}
.accordion-panel {
background: #fbfbfb;
line-height: 1.42;
font-size: 16px;
display: none;
padding: 15px 55px 15px 25px;
height: 0px;
max-height: 0px;
transition: max-height 0.5s ease-out;
}
.accordion-group .accordion:last-child .accordion-panel {
border-bottom: 1px solid #cccccc;
}
.accordion-panel.active {
border: 1px solid #cccccc;
border-bottom: 0px;
display: block;
/* This will need to be adjusted accordingly once you know the avg elem size */
/* For transition use only */
max-height: 800px;
height: auto;
overflow: hidden;
}
@media (max-width: 500px) {
.accordion-heading h3 > a {
font-size: 16px;
}
.accordion-panel {
font-size: 15px;
}
}
<section >
<div role="tablist" aria-multiselectable="true" id="accordion-mortgage">
<div >
<div role="tab">
<img src="https://i.postimg.cc/4xBs2Fjm/icons8-corgi-60.png" />
<h3 >
<a role="button" aria-expanded="false" data-parent="#accordion-mortgage" href="#">Lorem Ipsum</a>
</h3>
</div>
<div role="tabpanel" aria-expanded="false">
<div >I am the accordion's body content!</div>
</div>
</div>
<div >
<div role="tab">
<img src="https://i.postimg.cc/4xBs2Fjm/icons8-corgi-60.png" />
<h3 >
<a role="button" aria-expanded="false" data-parent="#accordion-mortgage" href="#">Lorem Ipsum</a>
</h3>
</div>
<div role="tabpanel" aria-expanded="false">
<div >
<p>
I am the accordion's body content! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
</p>
<p>
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
</ul>
</ul>
</div>
</div>
</div>
</div>
</section>
CodePudding user response:
I made the following change in your code and it's working fine now
- Added a boolean flag that saved the state of clicked accordion (before everything is closed)
- Used this flag to set the state of accordion (after everything is closed)
let isActive= panel.classList.contains("active");
document.querySelectorAll(".active").forEach(e => e.classList.remove("active"))
if(!isActive) {
panel.classList.add("active");
}
JS Fiddle: https://jsfiddle.net/aadeshk/Lb04v7qe/
CodePudding user response:
Since you are removing all active
classes then you can't really toggle them, unless you remember first the state.
document.addEventListener("DOMContentLoaded", () => {
const toggleClasses = (a) => {
let collapser = a.querySelector(".accordion-collapser");
let panel = a.querySelector(".accordion-panel");
let title = a.querySelector(".accordion-title");
var flag = panel.classList.contains("active")
document.querySelectorAll(".active").forEach(e => e.classList.remove("active"))
if (flag) {
panel.classList.remove("active");
} else {
panel.classList.add("active");
}
// title.classList.add("active");
}
document.querySelectorAll(".accordion").forEach((a) => {
a.addEventListener("click", () => toggleClasses(a), false)
});
})
html {
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.accordion-icon {
height: 50px;
width: 50px;
margin-left: 10px;
}
.accordion-group {
margin-bottom: 21px;
}
.accordion-heading {
padding: 0;
margin: 0;
border: 1px solid #cccccc;
border-top: 0px;
border-bottom: 0px;
position: relative;
display: flex;
align-items: center;
}
.accordion-heading:focus-within {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
outline-style: dotted;
outline-width: 2px;
}
.accordion-group .accordion-heading:first-child {
border-top: 1px solid #cccccc;
}
.accordion-group .accordion:last-child .accordion-heading {
border-bottom: 1px solid #cccccc;
}
.accordion-group .accordion:last-child .accordion-panel {
border-top: 0;
}
.accordion-title {
color: #363c3e;
line-height: 1.3em;
font-weight: 600;
font-size: 18px;
}
.accordion-heading a {
position: relative;
}
.accordion-title:hover {
color: #00a950;
cursor: pointer;
}
.accordion-heading:hover {
cursor: pointer;
}
.accordion-heading h3:after {
content: " ";
background: url("https://i.postimg.cc/13fCTwcc/collapse-icon.png");
position: absolute;
top: 50%;
right: 15px;
width: 37px;
height: 37px;
transform: translateY(-50%) rotate(0);
transition: transform 0.3s, -webkit-transform 0.3s;
}
.accordion-heading h3.active:after {
transform: translateY(-50%) rotate(-45deg);
}
.accordion-collapser {
display: inline-block;
color: #363c3e;
padding: 10px 55px 10px 15px;
text-decoration: none;
}
.accordion-collapser:focus {
outline: 0px;
}
.accordion-collapser:hover {
color: #00a950;
text-decoration: none;
}
.accordion-panel {
background: #fbfbfb;
line-height: 1.42;
font-size: 16px;
display: none;
padding: 15px 55px 15px 25px;
height: 0px;
max-height: 0px;
transition: max-height 0.5s ease-out;
}
.accordion-group .accordion:last-child .accordion-panel {
border-bottom: 1px solid #cccccc;
}
.accordion-panel.active {
border: 1px solid #cccccc;
border-bottom: 0px;
display: block;
/* This will need to be adjusted accordingly once you know the avg elem size */
/* For transition use only */
max-height: 800px;
height: auto;
overflow: hidden;
}
@media (max-width: 500px) {
.accordion-heading h3>a {
font-size: 16px;
}
.accordion-panel {
font-size: 15px;
}
}
<section >
<div role="tablist" aria-multiselectable="true" id="accordion-mortgage">
<div >
<div role="tab">
<img src="https://i.postimg.cc/4xBs2Fjm/icons8-corgi-60.png" />
<h3 >
<a role="button" aria-expanded="false" data-parent="#accordion-mortgage" href="#">Lorem Ipsum</a>
</h3>
</div>
<div role="tabpanel" aria-expanded="false">
<div >I am the accordion's body content!</div>
</div>
</div>
<div >
<div role="tab">
<img src="https://i.postimg.cc/4xBs2Fjm/icons8-corgi-60.png" />
<h3 >
<a role="button" aria-expanded="false" data-parent="#accordion-mortgage" href="#">Lorem Ipsum</a>
</h3>
</div>
<div role="tabpanel" aria-expanded="false">
<div >
<p>
I am the accordion's body content! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
</p>
<p>
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.
</p>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
</ul>
</ul>
</div>
</div>
</div>
</div>
</section>
CodePudding user response:
- You need to use
closest
to reach the parentaccordion
then get theaccordion-panel
- When you remove the active class from the other
panel
s afterfilter
ing the current panel - You can use
classList.toggle
instead ofif/else
condition.
document.addEventListener("DOMContentLoaded", () => {
const toggleClasses = (a) => {
let collapser = a.querySelector(".accordion-collapser");
let panel = a.closest('.accordion').querySelector(".accordion-panel");
let title = a.closest('.accordion').querySelector(".accordion-title");
[...document.querySelectorAll(".active")].filter(i => i !== panel).forEach(e => e.classList.remove("active"))
panel.classList.toggle("active")
}
document.querySelectorAll(".accordion").forEach((a) => {
a.addEventListener("click", () => toggleClasses(a), false)
});
})
html {
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.accordion-icon {
height: 50px;
width: 50px;
margin-left: 10px;
}
.accordion-group {
margin-bottom: 21px;
}
.accordion-heading {
padding: 0;
margin: 0;
border: 1px solid #cccccc;
border-top: 0px;
border-bottom: 0px;
position: relative;
display: flex;
align-items: center;
}
.accordion-heading:focus-within {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
outline-style: dotted;
outline-width: 2px;
}
.accordion-group .accordion-heading:first-child {
border-top: 1px solid #cccccc;
}
.accordion-group .accordion:last-child .accordion-heading {
border-bottom: 1px solid #cccccc;
}
.accordion-group .accordion:last-child .accordion-panel {
border-top: 0;
}
.accordion-title {
color: #363c3e;
line-height: 1.3em;
font-weight: 600;
font-size: 18px;
}
.accordion-heading a {
position: relative;
}
.accordion-title:hover {
color: #00a950;
cursor: pointer;
}
.accordion-heading:hover {
cursor: pointer;
}
.accordion-heading h3:after {
content: " ";
background: url("https://i.postimg.cc/13fCTwcc/collapse-icon.png");
position: absolute;
top: 50%;
right: 15px;
width: 37px;
height: 37px;
transform: translateY(-50%) rotate(0);
transition: transform 0.3s, -webkit-transform 0.3s;
}
.accordion-heading h3.active:after {
transform: translateY(-50%) rotate(-45deg);
}
.accordion-collapser {
display: inline-block;
color: #363c3e;
padding: 10px 55px 10px 15px;
text-decoration: none;
}
.accordion-collapser:focus {
outline: 0px;
}
.accordion-collapser:hover {
color: #00a950;
text-decoration: none;
}
.accordion-panel {
background: #fbfbfb;
line-height: 1.42;
font-size: 16px;
display: none;
padding: 15px 55px 15px 25px;
height: 0px;
max-height: 0px;
transition: max-height 0.5s ease-out;
}
.accordion-group .accordion:last-child .accordion-panel {
border-bottom: 1px solid #cccccc;
}
.accordion-panel.active {
border: 1px solid #cccccc;
border-bottom: 0px;
display: block;
/* This will need to be adjusted accordingly once you know the avg elem size */
/* For transition use only */
max-height: 800px;
height: auto;
overflow: hidden;
}
@media (max-width: 500px) {
.accordion-heading h3 > a {
font-size: 16px;
}
.accordion-panel {
font-size: 15px;
}
}
<section >
<div role="tablist" aria-multiselectable="true" id="accordion-mortgage">
<div >
<div role="tab">
<img src="https://i.postimg.cc/4xBs2Fjm/icons8-corgi-60.png" />
<h3 >
<a role="button" aria-expanded="false" data-parent="#accordion-mortgage" href="#">Lorem Ipsum</a>
</h3>
</div>
<div role="tabpanel" aria-expanded="false">
<div >I am the accordion's body content!</div>
</div>
</div>
<div >
<div role="tab">
<img src="https://i.postimg.cc/4xBs2Fjm/icons8-corgi-60.png" />
<h3 >
<a role="button" aria-expanded="false" data-parent="#accordion-mortgage" href="#">Lorem Ipsum</a>
</h3>
</div>
<div role="tabpanel" aria-expanded="false">
<div >
<p>
I am the accordion's body content! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
</p>
<p>
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
</ul>
</ul>
</div>
</div>
</div>
</div>
</section>