Reputation: 105
I would like to make it for if one of these panels is open, it'll close when you've clicked on another panel. I looked at some other questions similar to mine but it seems they all uses a different method for the panels.
Heres a Fiddle of it: https://jsfiddle.net/3q87y2u8/
Heres the JS:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
}
I'd also like to get the '+' to change color when hovered over. Kinda like the text currently does. I cant seem to call it out right in the CSS though.
Thank You!
Upvotes: 2
Views: 2336
Reputation: 3510
A more readable and more efficient way would be to simply hold a reference to the previously toggled accordion item.
When onclick is called, you will first want to check if cur is the clicked on panel. That way, toggling cur will just close it. Next, set cur to null, so it won't open up the previous panel when you click a different panel. Then make sure to return out of the function. Note that you have to do this before the nonnull check for cur.
Otherwise, check the now active item to current to make sure it is not null, then toggle both cur and this. Wrap it up by setting cur to this, so next time it can do it all over again!
Also, here's a fiddle: https://jsfiddle.net/d7smh9ru/4/
var cur = null;
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
// Only close already open panel
if (cur == this) {
toggleItem(cur);
cur = null;
return;
}
// Close current panel, and open this panel
if (cur) {
toggleItem(cur);
}
toggleItem(this);
cur = this;
}
}
function toggleItem(item) {
item.classList.toggle("active");
var panel = item.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
Upvotes: 1
Reputation: 673
You can get the element that is currently active and then remove the active class like so:
// Is an array of elements matching the selector
var active = document.getElementsByClassName('active');
// If there are any matching elements and it is not the same one that has just been clicked on
if (active.length && active[0] !== this) {
active[0].classList.remove('active');
}
One side comment (you are of course free to ignore!): for the max-height you don't need to figure out the height (unless of course it interferes with how you have your page structure already set up). You can just use some large height and since it's the max-height you are setting and not the height it will automatically go to its natural height. You can also achieve this using just CSS, including the animation, and updating the hover text on the plus sign is included below as well.
// Set regular state height to 0 and add a transition effect
.panel {
max-height: 0px;
transition: max-height .25s ease;
}
// For whatever button is active find the adjacent panel and set its max height and a transition effect
.active + .panel {
max-height: 250px;
transition: max-height .25s ease;
}
// Here is where you change the color of the active and hovered plus/minus sign
button.accordion.active:after,
button.accordion:hover:after {
color: red;
}
https://jsfiddle.net/xz2mpzg2/1/ https://jsfiddle.net/xz2mpzg2/2/
Upvotes: 1
Reputation: 1270
I believe you're looking for something like this: JSFiddle.
I'll go through each change I made, and each statement is also commented, but as a general principal, I changed your code to use a wrapper div
around each group of buttons/text, and I put the text inside an inner div
so it would respect max-height
. Instead of the event handler modifying its next sibling, which would fail, e.g., if your paragraphs were individually enclosed in p
tags and not line-broken using br
tags inside your p
s. This also made it trivial to implement the hover-changes-plus-color effect, using the parent's :hover
pseudoclass.
So, if you change your button/text groups from:
<button class="accordion">Home</button>
<div class="panel">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
to:
<div class="panel">
<button class="accordion">Home</button>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
and update your CSS, changing your panel
class to content
(previously beginning on line 50 of your JSFiddle):
.content {
overflow: hidden;
transition: max-height 0.2s ease-out;
padding-left: 50px;
background-color: #f6f6f6;
width: 220px;
font-size: 10px;
max-height: 100px;
}
and use CSS to set the max-height to 0 on non-opened panels
div.panel:not(.opened) > .content {
max-height: 0;
}
You can then set your event listeners on the buttons, and simply toggle the parent's opened
class:
document.querySelectorAll('.accordion')
.forEach(element => {
element.onclick = function() {
// first, iterate over other open panels and close them...
// note: using Array.from(...) so we can filter the result below.
Array.from(document.querySelectorAll('.panel.opened'))
// ...but skip the clicked panel, which will be dealt with below
// [edit] we filter here so we can close an already-open panel
.filter(panel => panel !== this.parentNode)
.forEach(panel => {
// toggle the 'opened' class, forcing `false`
panel.classList.toggle('opened', false)
// and remove styling that was set on the element itself
panel.querySelector('.content').style.maxHeight = null;
});
// now toggle the clicked panel's 'opened' class and capture its new value
const toggled = this.parentNode.classList.toggle('opened');
// reference the new div.content, inside the button's parent
const content = this.parentNode.querySelector('.content');
// and either fix its max height or unset it, depending on new state
content.style.maxHeight = toggled? `${content.scrollHeight}px` : null;
}
});
Finally, if you want to change the '+' color on highlight, now you can do so using the :hover
pseudoclass on .panel
:
div.panel:hover > button.accordion:after {
color: red;
}
Upvotes: 0
Reputation: 8294
Simply with jQuery you could easily use:
A.) .toggle();
B.) .change();
C.) .addClass(); / removeClass();
Upvotes: 0
Reputation: 688
shrink all the divs right after the button click. The jsfiddle here https://jsfiddle.net/3q87y2u8/3/
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
var panels=document.getElementsByClassName("panel");
for (j = 0; j < panels.length; j++) {
panels[j].style.maxHeight = null;
}
var panel = this.nextElementSibling;
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
}
Upvotes: 0