Create A Simple Accordion Menu Using Vanilla JavaScript
Tags: html, css, javascript, programming
Published: 2022-04-25 12:57:15

---
canonical_url: https://softauthor.com/accordion-menu-in-vanilla-javascript/
---

By the end of this tutorial, you’re going to be building a simple accordion menu, just using vanilla javascript of course, with the combination of HTML and CSS.

What are we building?

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion.gif)

- [Accordion Menu HTML Structure](https://softauthor.com/accordion-menu-in-vanilla-javascript/#accordion-menu-html-code)
- [Accordion Menu CSS](https://softauthor.com/accordion-menu-in-vanilla-javascript/#accordion-menu-css-code)
- [Accordion Menu JavaScript](https://softauthor.com/accordion-menu-in-vanilla-javascript/#accordion-menu-javascript-code)
- [Accordion Menu Collapsable Icon](https://softauthor.com/accordion-menu-in-vanilla-javascript/#accordion-menu-collapsable-icon)
- [Open One Accordion Menu At A Time](https://softauthor.com/accordion-menu-in-vanilla-javascript/#open-one-accordion-menu-at-a-time)
- [Accordion Menu Transition](https://softauthor.com/accordion-menu-in-vanilla-javascript/#accordion-menu-transition-animation)

###Accordion Menu HTML Structure

The HTML code of the Accordion Menu is pretty straightforward.

I have a simple **section** element with an id called **#accordion-container**.

Inside that, add three divs with the class name **.menu** and each contains two HTML elements: **.header** and **.content**.

Header element also contains **.title** and **.icon** elements.

```HTML
<section id="accordion">

    <div class="menu">
        <div class="header">
            <div class="title">Title 1</div>
            <span class="icon">+</span>
        </div>
        <div class="content">
            Content 1
        </div>
    </div>

    <div class="menu">
        <div class="header">
            <div class="title">Title 2</div>
            <span class="icon">+</span>
        </div>
        <div class="content">
            Content 2
        </div>
    </div>

    <div class="menu">
        <div class="header">
            <div class="title">Title 3</div>
            <span class="icon">+</span>
        </div>
        <div class="content">
            Content 3
        </div>
    </div>

</section>
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-HTML.png)

###Accordion Menu CSS
Let’s add some CSS to make to match the final result.

Define a CSS rule for **#accordion** with a **max-width to 500px** and make it center to the screen horizontally using **margin: 0 auto** property.

After that, add borders all-around to each **.menu** item which is the clickable area of the accordion menu.

```CSS
#accordion {
  max-width: 500px;
  margin:0 auto;
}

.menu {
  border:1px solid #008080;
  border-bottom:none;
}

 .menu:last-child {
  border-bottom:1px solid #808080;
}
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-CSS.png)

Using flexbox, lets move the “+” icon to the right and keep the title on the left.

>_Recommended_
>_[Must-Know CSS Flexbox Responsive Multi-Column Layout Explained](https://softauthor.com/css-flexbox-responsive-multi-column-layout/)_

```CSS

.header {
  display:flex;
  padding:10px; 
  cursor: pointer;
}

.title {
  flex:1;
}

.icon {
  width:20px;
} 

 .content {
  padding:10px;
} 
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-CSS-Collasable-icon-right-align.png)

Finally, hide the **.content** by default and I will show you how to toggle its visibility using JavaScript in just a moment.

```CSS
.content {
  display:none;
  padding:10px;
} 
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-CSS-Collasable-icon-right-align.png)

That looks much better.

Now let’s move into the fun part.

###Accordion Menu JavaScript

The first step is to get all the DOM references of the header as well as the content and assign them to the constant arrays called **headers** and **contents** respectively.

```JavaScript
const headers = document.getElementsByClassName("header"),
      contents = document.getElementsByClassName("content");
```

Secondly, iterate through the **headers** array using **for loop** and attach a **click** event to each header inside with a callback arrow function.

>_Recommended_ 
>_[JavaScript For Loop Click Event ← Issues & Solutions Explained](https://softauthor.com/javascript-for-loop-click-event-issues-solutions/)_

```JavaScript
for (let i = 0; i < headers.length; i++) {
     headers[i].addEventListener("click", () => {
     });
}
```

Finally, toggle the content visibility by using the display CSS property on it. I used the ternary operator here to achieve this but you can also use the regular if statement.

```JavaScript
contents[i].style.display = contents[i].style.display == "block" ? "none" : "block";
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/Simple-JavaScript-Accordion-Menu.gif)

This works great, but the icon is not changing when the menu opens or collapses, so let’s fix that next.

###Accordion Collapsable Icon

Similar to what I’ve done toggling the visibility of the content, show the “+” icon by default and replace it with “-” icon when the accordion menu opens otherwise replace it with the “+” sign when it collapses.

Pretty straightforward.

To do that, get the DOM references of all the icons and assign them to a constant array called icons.

```JavaScript
 const headers = document.getElementsByClassName("header"),
        contents = document.getElementsByClassName("content"),
        icons = document.getElementsByClassName("icon");
```

After that, include the following line inside the click event.

```JavaScript
 for (let i = 0; i < headers.length; i++) {
        headers[i].addEventListener("click", () => {
            contents[i].style.display = contents[i].style.display == "block" ? "none" : "block";
            icons[i].innerHTML = contents[i].style.display == "block" ? "-" : "+";
        });
    }
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-Collapse-Icon.gif)

###Open One Accordion Menu At A Time

What if you want to open only the clicked accordion menu item and have the others collapse.

To do that, iterate through the contents array inside the click event and check to see if the index value of the headers array matches the index value of the contents array which is i == j.

Now, move the two lines of code inside the condition block, but this time with the incrementor j instead of i.

In the else statement, collapse all other accordion menus using the display none CSS property, as well as reset the default icon to  “+”.

```JavaScript
for (let i = 0; i < headers.length; i++) {
        headers[i].addEventListener("click", () => {
            for (let j = 0; j < contents.length; j++) {
                if (i == j) {
                    contents[j].style.display = contents[j].style.display == "block" ? "none" : "block";
                    icons[j].innerHTML = contents[j].style.display == "block" ? "-" : "+";
                } else {
                    contents[j].style.display = "none";
                    icons[j].innerHTML = "+";
                }
            }
        });
    }
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-Open-One-At-A-time.gif)

###Accordion Menu Transition

At this stage, the accordion menu opens and collapses without any type of transition. 

Let’s change that.

Replace the .content CSS rule with the code below. As you can see, I got rid of the display: none and I used the height and overflow properties to hide the content element by default.

```CSS
.content {
  max-height: 0;
  transition: max-height 1s ease-out;
  overflow: hidden;
  padding:0;
}
```

Then, create another CSS rule called **.content-transition**.

```CSS
.content-transition {
  max-height: 1500px;
  transition: max-height 3s ease-in;
} 
```

Toggle the .content-transition class if one of the accordion menu items is clicked and remove the .content-transition class from other elements.

```JavaScript
for (let i = 0; i < headers.length; i++) {
        headers[i].addEventListener("click", () => {

            for (let j = 0; j < contents.length; j++) {
                if (i == j) {
                    contents[j].classList.toggle("content-transition");
 
                } else {
                    contents[j].classList.remove("content-transition");
                }
            }

        });
    }
```

As you can see, at this stage the collapsible icon no longer works as expected. That’s because previously I used the display CSS property to hide and show the content element. That is no longer the case as I got rid of the display: none property from the content CSS rule.

Instead, check to see if the height of the content is higher than 0.

```JavaScript
 for (let i = 0; i < headers.length; i++) {
        headers[i].addEventListener("click", () => {

            for (let j = 0; j < contents.length; j++) {
                if (i == j) {
                    icons[j].innerHTML = contents[j].getBoundingClientRect().height === 0 ? "-" : "+";
                    contents[j].classList.toggle("content-transition");
                } else {
                    icons[j].innerHTML = "+";
                    contents[j].classList.remove("content-transition");
                }
            }

        });
    }
```

I removed the padding from the .content class in order to have a more smooth transition and the workaround is to add another HTML element, such as p tag inside .content, and put all the code inside.

```HTML
<div class="content">
   <p>
      Content 1
   </p>
</div>
...
```

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion-Menu-No-Padding-Content.png)

As you can see, there is no space between the content and the border as I no longer have the padding to the .content due to the overflow issue.

Instead, wrap the actual content with the p tag to all the .content elements and give the margin to the p tag.

```CSS
p {
  margin:10px;
} 
```

Giving margin or padding to the p tag won’t affect the animation as it’s inside the main .content container.

![alt text](https://softauthor.com/wp-content/uploads/2022/02/JavaScript-Accordion.gif)

There you have it.

You can download the source code [here](https://softauthor.com/download-javascript-accordion-menu-code%EF%BF%BC/).

Thank you for reading and if you’ve any questions, feel free to comment below.

Happy coding!