Construct an Animated JavaScript Accordion Element, With Overlapping Panels

On this new tutorial, we’ll discover ways to construct an animated JavaScript accordion element with overlapping panels.
We received’t focus a lot on accessibility on this tutorial, so exploring find out how to make this element extra accessible could be a sound subsequent step.
Our Accordion Element
Right here’s what we will create (click on on a panel to check the habits):
1. Start With the Web page Markup
Inside a container, we’ll place an inventory of panels.
Every panel may have a title and content material. Throughout the title, we’ll add a Shut button from the place we are able to shut the energetic panel.
Right here’s the required construction:
1 | class=“accordion-wrapper”> |
2 | |
3 | |
4 | class=“accordion-title”>… |
5 | class=“accordion-content”> |
6 | class=“inside”>… |
7 | |
8 | |
9 | |
10 | class=“accordion-title”>… |
11 | class=“accordion-content”> |
12 | class=“inside”>… |
13 | |
14 | |
15 | |
16 | class=“accordion-title”>… |
17 | class=“accordion-content”> |
18 | class=“inside”>… |
19 | |
20 | |
21 | |
22 | class=“accordion-title”>… |
23 | class=“accordion-content”> |
24 | class=“inside”>… |
25 | |
26 | |
27 | |
28 |
Preliminary Accordion State/Lively Gadgets
By default, all panels might be collapsed.
To forestall this habits, we’ve to assign the energetic class to a number of panels like this:
1 | |
2 | |
3 |
A number of Open Panels
There’s additionally the choice to have a couple of panel open concurrently with out one collapsing when the opposite is open. To allow this, we must always add the data-multiple=”true” attribute to the accordion wrapper like this:
1 | class=“accordion-wrapper” data-multiple=“true”>… |
2. Add the CSS
Let’s now consider the important thing kinds—a lot of the different kinds aren’t something particular, so let’s go away them for now:
- To make the panels overlap and create a special accordion format in comparison with the usual ones, we’ll give them a destructive prime margin and an equal backside padding. Just for the primary and final objects, we’ll cancel the highest margin and backside padding respectively.
- To cover the content material of every panel, we’ll give them top: 0 and overflow: hidden. Then, as we’ll see later, by way of JavaScript, we’ll recalculate their top and reveal them easily. Simply, be aware that we’ll additionally use top: 0 !essential to reset the peak to 0 and override the JavaScript kinds for a beforehand energetic panel.
- To open the modal, the entire panel space might be clickable. To make it clear, we’ll assign cursor: pointer to all panels. Quite the opposite, when a panel is open, we are able to shut it solely by way of the shut button. At this second, solely this button may have cursor: pointer whereas the panel may have cursor: default.
Right here’s part of the required kinds:
1 | /*CUSTOM STYLES HERE*/ |
2 | |
3 | .accordion-wrapper li { |
4 | padding: 0 20px 100px; |
5 | cursor: pointer; |
6 | border-top-left-radius: var(–accordion-radius); |
7 | border-top-right-radius: var(–accordion-radius); |
8 | background: var(–accordion-bg-color); |
9 | transition: all 0.2s ease-out; |
10 | } |
11 | |
12 | .accordion-wrapper li:not(:first-child) { |
13 | margin-top: -100px; |
14 | border-top: 2px stable var(–light-cyan); |
15 | } |
16 | |
17 | .accordion-wrapper li:nth-last-child(2), |
18 | .accordion-wrapper li:last-child { |
19 | border-bottom-left-radius: var(–accordion-radius); |
20 | border-bottom-right-radius: var(–accordion-radius); |
21 | } |
22 | |
23 | .accordion-wrapper li:last-child { |
24 | padding-bottom: 0; |
25 | } |
26 | |
27 | .accordion-wrapper:not([data-multiple=“true”]) li.energetic { |
28 | border-top-color: var(–accordion-active-bg-color); |
29 | } |
30 | |
31 | .accordion-wrapper li.energetic { |
32 | cursor: default; |
33 | coloration: var(–white); |
34 | background: var(–accordion-active-bg-color); |
35 | } |
36 | |
37 | .accordion-wrapper li:not(.energetic) .accordion-content { |
38 | top: 0 !essential; |
39 | } |
40 | |
41 | .accordion-wrapper .accordion-content { |
42 | top: 0; |
43 | overflow: hidden; |
44 | transition: top 0.3s; |
45 | } |
46 | |
47 | .accordion-wrapper .inside { |
48 | padding-bottom: 40px; |
49 | } |
50 | |
51 | @media (min-width: 700px) { |
52 | .accordion-wrapper li { |
53 | padding-left: 60px; |
54 | padding-right: 60px; |
55 | } |
56 | |
57 | .accordion-wrapper .inside { |
58 | max-width: 85%; |
59 | } |
60 | } |
3. Add the JavaScript
The way in which we’ll animate every panel and obtain a slide impact just like jQuery’s slideToggle() perform is by benefiting from the scrollHeight property.
This property measures the peak of a component’s content material, together with content material not seen on the display on account of overflow. In our case, we’ll must calculate that worth for the .accordion-content components which have top: 0 and overflow: hidden by default.
When DOM Prepared
As a primary motion, when the DOM is prepared, we’ll verify if there are any energetic panels, and if that’s the case, we’ll set the peak for the .accordion-content component of every energetic panel equal to its scrollHeight property worth.
Right here’s the associated JavaScript code:
1 | const activeItems = accordionWrapper.querySelectorAll(“li.energetic“); |
2 | |
3 | if (activeItems) { |
4 | activeItems.forEach(perform (merchandise) { |
5 | const content material = merchandise.querySelector(“.accordion-content“); |
6 | content material.type.top = `${content material.scrollHeight}px`; |
7 | }); |
8 | } |
Toggle Accordion Panels
Subsequent, every time we click on on a panel, we’ll do the next issues:
- Test if we clicked on the shut button. If that occurs and the panel is open, we’ll shut it by eradicating the energetic class and ignoring all the subsequent steps.
- Test if we’ve set the choice to have a number of panels open collectively. If that isn’t the case and there’s an energetic panel, we’ll shut it.
- Add the energetic class to that panel.
- Set the peak for the .accordion-content component of this panel equal to its scrollHeight property worth.
Right here’s the JavaScript code that implements all that habits:
1 | const accordionWrapper = doc.querySelector(“.accordion-wrapper“); |
2 | const objects = accordionWrapper.querySelectorAll(“li“); |
3 | const multiple_open = accordionWrapper.dataset.a number of; |
4 | const ACTIVE_CLASS = “energetic“; |
5 | |
6 | objects.forEach(perform (merchandise) { |
7 | merchandise.addEventListener(“click on“, perform (e) { |
8 | // 1 |
9 | const goal = e.goal; |
10 | if ( |
11 | (goal.tagName.toLowerCase() === “button“ || goal.closest(“button“)) && |
12 | merchandise.classList.incorporates(ACTIVE_CLASS) |
13 | ) { |
14 | merchandise.classList.take away(ACTIVE_CLASS); |
15 | return; |
16 | } |
17 | |
18 | // 2 |
19 | if ( |
20 | “true“ !== multiple_open && |
21 | doc.querySelector(“.accordion-wrapper li.energetic“) |
22 | ) { |
23 | doc |
24 | .querySelector(“.accordion-wrapper li.energetic“) |
25 | .classList.take away(ACTIVE_CLASS); |
26 | } |
27 | |
28 | // 3 |
29 | merchandise.classList.add(ACTIVE_CLASS); |
30 | |
31 | // 4 |
32 | const content material = merchandise.querySelector(“.accordion-content“); |
33 | content material.type.top = `${content material.scrollHeight}px`; |
34 | }); |
35 | }); |
Conclusion
Finished! I hope you loved the JavaScript accordion we constructed and realized one or two new issues.
Earlier than closing, let’s recall our essential creation right this moment:
As at all times, thanks quite a bit for studying!