Reputation: 79
I want to have multiple dropdowns in one component using one variable to display or not and also clicking away from their div to close them:
<div class="dropdown">
<button @click.prevent="isOpen = !isOpen"></button>
<div v-show="isOpen">Content</div>
</div>
// second dropdown in same component
<div class="dropdown">
<button @click.prevent="isOpen = !isOpen"></button>
<div v-show="isOpen">Content</div>
</div>
data() {
return {
isOpen: false
}
},
watch: {
isOpen(isOpen) {
if(isOpen) {
document.addEventListener('click', this.closeIfClickedOutside)
}
}
},
methods: {
closeIfClickedOutside(event){
if(! event.target.closest('.dropdown')){
this.isOpen = false;
}
}
}
But now when I click one dropdown menu it displays both of them. I am kinda new to vue and cant find way to solve this
Upvotes: 2
Views: 61
Reputation: 106
Make an array and loop through it, much easier that way.
<template>
<div id="app">
<div class="dropdown" v-for="(drop, index) in dropData" :key="index">
<button @click="openDropdown(index);">{{ drop.title }}</button>
<div v-show="isOpen === index">{{ drop.content }}</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isOpen: null,
dropData: [
{
title: "Hey",
content: "Hey it's content 1"
},
{
title: "Hey 2",
content: "Hey it's content 2"
},
{
title: "Hey 3",
content: "Hey it's content 3"
},
]
};
},
methods: {
openDropdown(idx){
if (this.isOpen === idx) {
this.isOpen = null;
} else {
this.isOpen = idx;
}
}
}
};
</script>
Upvotes: 1
Reputation: 138276
To use just one variable for this, the variable needs to identify which dropdown is open, so it can't be a Boolean. I suggest storing the index (e.g., a number) in the variable, and conditionally render the selected dropdown by the index:
Declare a data property to store the selected index:
export default {
data() {
return {
selectedIndex: null
}
}
}
Update closeIfClickedOutside()
to clear the selected index, thereby closing the dropdowns:
export default {
methods: {
closeIfClickedOutside() {
this.selectedIndex = null
}
}
}
In the template, update the click
-handlers to set the selected index:
<button @click.stop="selectedIndex = 1">Open 1</button>
<button @click.stop="selectedIndex = 2">Open 2</button>
Also, update the v-show
condition to render based on the index:
<div v-show="selectedIndex === 1">Content 1</div>
<div v-show="selectedIndex === 2">Content 2</div>
Also, don't use a watcher to install a click
-handler on the document
because we want to know about the outside-clicks when this component is rendered. It would be more appropriate to add the handler in the mounted
hook, and then remove in the beforeDestroy
hook:
export default {
mounted() {
document.addEventListener('click', this.closeIfClickedOutside)
},
beforeDestroy() {
document.removeEventListener('click', this.closeIfClickedOutside)
},
}
Upvotes: 1