How to create a simple accordion menu with Nuxt.js

We will be creating a series of accordion menus that can open each menu individually when clicked.

If you would like to jump into the code without reading this post, you are welcome to have a look at https://github.com/MTipps/nuxtjs-simple-accordion-menu.

Project Setup

Open up your terminal and navigate to a folder where you would like to save your project. I will be using a folder called nuxtjs-simple-accordion-menu under my Projects folder.

To create our application, we will be using the create-nuxt-app (https://github.com/nuxt/create-nuxt-app) package to start our project. In the folder, you have navigated to previously, run the following command to create your application.

yarn create nuxt-app <project-name>

It will start by asking you some questions, and you can select the following for our project:
Project Name: nuxtjs-simple-accordion-menu
Programming Language: Javascript
Package manager: I prefer yarn, but you are welcome to choose npm as your package manager.
UI Framework: I will be using Tailwind CSS for a few styling changes.
Nuxt.js modules: None (We will not need this)
Linting tools: I am selecting ESLint as I prefer it, you are welcome to choose the one you like.
Testing framework: I am not going to select one for this example, but remember unit tests are essential!
Rendering mode: Universal
Deployment target: Static (Static/JAMStack hosting)
Development tools: None (If you are using Visual Studio Code please select jsconfig.json)
Continuous integration: None
Version control system: Git (Only if you want to save it to your source control)

Successful project creation

In your terminal, navigate into the project folder we just created:

cd nuxtjs-simple-accordion-menu

Then run either of the following to start your server:

yarn dev

or

npm dev

If everything went well, you would now be able to see a similar page when you navigate to http://localhost:3000/ in your browser.

Browser view of successful server start

Now that our project is ready to go, you can open it in your favourite IDE, mine being IntelliJ Idea, and follow along.

Creating the accordion menus

In your IDE have a look at the project structure. I am not going to go into detail about this, but have a look at the docs for more information (https://nuxtjs.org/guide/directory-structure).

We will be using the index.vue file under the pages directory, as seen at http://localhost:3000/.

index.vue file we will be using

Cleanup this file by removing all the code and replacing it with:

<template> 
<div></div>
</template>
<script>
export default {

}
</script>

Now that we have a blank slate lets add the data for our accordion menu, by replacing the code in the script tags with:

<template>
<div></div>
</template>
<script>
export default {
data () {
return {
harryPotterCharacters: [
{
characterName: 'Harry Potter',
characterShortBio: 'Harry James Potter (b. 31 July 1980) was an English half-blood wizard, one of the most ' +
'famous wizards of modern times. He was the only child and son of James and Lily Potter (née Evans), ' +
'both members of the original Order of the Phoenix. Harry\'s birth was overshadowed by a prophecy, ' +
'naming either himself or Neville Longbottom as the one with the power to vanquish Lord Voldemort. ' +
'After half of the prophecy was reported to Voldemort courtesy of Severus Snape, Harry was chosen as the ' +
'target due to his many similarities with the Dark Lord. This caused the Potter family to go into hiding. ' +
'Voldemort made his first vain attempt to circumvent the prophecy when Harry was a year and three months old. ' +
'During this attempt, he murdered Harry\'s parents as they tried to protect him, but this unsuccessful ' +
'attempt to kill Harry led to Voldemort\'s first downfall. This downfall marked the end of the First ' +
'Wizarding War, and to Harry henceforth being known as the "Boy Who Lived."'
},
{
characterName: 'Hermione Granger',
characterShortBio: 'Hermione Jean Granger (b. 19 September, 1979) was an English Muggle-born witch born to ' +
'Mr and Mrs Granger. At the age of eleven, she learned about her magical nature and had been accepted into ' +
'Hogwarts School of Witchcraft and Wizardry. Hermione began attending Hogwarts in 1991 and was Sorted into ' +
'Gryffindor House. She possessed a brilliant academic mind and proved to be a gifted student in almost ' +
'every subject that she studied.'
},
{
characterName: 'Ron Weasley',
characterShortBio: 'Ronald Bilius "Ron" Weasley (b. 1 March, 1980) was an English pure-blood wizard, the ' +
'sixth and youngest son of Arthur and Molly Weasley (née Prewett). He was also the younger brother of ' +
'Bill, Charlie, Percy, Fred, George, and the elder brother of Ginny. Ron and his brothers and sister ' +
'lived at the Burrow, on the outskirts of Ottery St Catchpole.'
}
]
}
}
}
</script>

What we did: We created a simple array of objects, and each item holds the characters name and a short biography.

Now it does not look like we have done much, but your data is available immediately. Install Vue Devtools for either Chrome or Firefox by going to https://github.com/vuejs/vue-devtools. Once installed, you can open the browser developer tools as per usual, and you will now see you have a new option called Vue. Once you open it and continue through the structure, you will see your array of Harry Potter characters on the right.

Vue developer tools with our array of data

Now to show your data without us having to go into the developer tools change the code in your template tag with the following:

<template>
<div class="m-20">
<div
v-for="(character) in harryPotterCharacters"
:key="character.characterName"
>
<p class="bg-gray-800 text-white p-6 cursor-pointer">
{{ character.characterName }}
</p>
<div class="bg-gray-100 p-6">
<p>{{ character.characterShortBio }}</p>
</div>
</div>
</div>
</template>

What we did: We updated a simple div with a v-for, so we can loop through our array of objects and then display the relevant data. I also added some Tailwind CSS classes to make it look a bit more pleasant.

The start of our accordion menu

Now for the functionality to open and close the accordion items, we start by changing our code in the template tag to the following:

<template>
<div class="m-20">
<div
v-for="(character, index) in harryPotterCharacters"
:key="character.characterName"
>
<p class="bg-gray-800 text-white p-6 cursor-pointer" @click="characterItemClick(index)">
{{ character.characterName }}
</p>
<div :data-character-id="index" class="bg-gray-100 p-6">
<p>{{ character.characterShortBio }}</p>
</div>
</div>
</div>
</template>

What we did:

  1. We have now added an index in the v-for. The index will automatically assign the correct index number of the accordion item.
  2. We added a click event listener to our character name div. When you click the character, it will pass the accordion item index which lets us know which character biography to show.
  3. We add this to our div that contains the character biography via a data attribute called data-character-id. We will now be able to know which div to look for when the character gets clicked.

For the click listener to work, we need to add the event in methods, after your ending curly brace for your data() in the script tag add a comma and the following:

methods: {
characterItemClick (characterIndex) {
const characterInfoElement = document.querySelectorAll('[data-character-id="' + characterIndex + '"]')[0]
if (characterInfoElement.classList.contains('block')) {
characterInfoElement.classList.remove('block')
characterInfoElement.classList.add('hidden')
} else {
characterInfoElement.classList.remove('hidden')
characterInfoElement.classList.add('block')
}
}
}

What we did: We are searching the whole document for the data attribute called data-character-id with the index that we passed through. We then check to see if it is hidden or not. We then add and remove classes on that element. Please note, I am using standard Tailwind CSS classes for display: none and display: block. If you did not choose to use Tailwind CSS, then you will have to create these classes on your own in style tags at the bottom of the page.

When you now go to http://localhost:3000/, you will have a functioning accordion menu with each item working on their own to open and close.

Here you can see the index.vue file as a whole.

<template>
<div class="m-20">
<div
v-for="(character, index) in harryPotterCharacters"
:key="character.characterName"
>
<p class="bg-gray-800 text-white p-6 cursor-pointer" @click="characterItemClick(index)">
{{ character.characterName }}
</p>
<div :data-character-id="index" class="bg-gray-100 p-6 hidden">
<p>{{ character.characterShortBio }}</p>
</div>
</div>
</div>
</template>

<script>
export default {
data () {
return {
harryPotterCharacters: [
{
characterName: 'Harry Potter',
characterShortBio: 'Harry James Potter (b. 31 July 1980) was an English half-blood wizard, one of the most ' +
'famous wizards of modern times. He was the only child and son of James and Lily Potter (née Evans), ' +
'both members of the original Order of the Phoenix. Harry\'s birth was overshadowed by a prophecy, ' +
'naming either himself or Neville Longbottom as the one with the power to vanquish Lord Voldemort. ' +
'After half of the prophecy was reported to Voldemort courtesy of Severus Snape, Harry was chosen as the ' +
'target due to his many similarities with the Dark Lord. This caused the Potter family to go into hiding. ' +
'Voldemort made his first vain attempt to circumvent the prophecy when Harry was a year and three months old. ' +
'During this attempt, he murdered Harry\'s parents as they tried to protect him, but this unsuccessful ' +
'attempt to kill Harry led to Voldemort\'s first downfall. This downfall marked the end of the First ' +
'Wizarding War, and to Harry henceforth being known as the "Boy Who Lived."'
},
{
characterName: 'Hermione Granger',
characterShortBio: 'Hermione Jean Granger (b. 19 September, 1979) was an English Muggle-born witch born to ' +
'Mr and Mrs Granger. At the age of eleven, she learned about her magical nature and had been accepted into ' +
'Hogwarts School of Witchcraft and Wizardry. Hermione began attending Hogwarts in 1991 and was Sorted into ' +
'Gryffindor House. She possessed a brilliant academic mind and proved to be a gifted student in almost ' +
'every subject that she studied.'
},
{
characterName: 'Ron Weasley',
characterShortBio: 'Ronald Bilius "Ron" Weasley (b. 1 March, 1980) was an English pure-blood wizard, the ' +
'sixth and youngest son of Arthur and Molly Weasley (née Prewett). He was also the younger brother of ' +
'Bill, Charlie, Percy, Fred, George, and the elder brother of Ginny. Ron and his brothers and sister ' +
'lived at the Burrow, on the outskirts of Ottery St Catchpole.'
}
]
}
},
methods: {
characterItemClick (characterIndex) {
const characterInfoElement = document.querySelectorAll('[data-character-id="' + characterIndex + '"]')[0]
if (characterInfoElement.classList.contains('block')) {
characterInfoElement.classList.remove('block')
characterInfoElement.classList.add('hidden')
} else {
characterInfoElement.classList.remove('hidden')
characterInfoElement.classList.add('block')
}
}
}
}
</script>

A front-end developer who produces high-quality websites and exceptional user experience.