Without adding extra dependencies to your project
I found myself running into this problem recently where I had a toggle switch to display a menu — but when I clicked on another part of the page, the menu did not disappear. I realised there are libraries out there such as this one https://github.com/simplesmiler/vue-clickaway but I’d rather not pull in another dependency if I don’t have to. I thought I would share one dependency-free way to hide the menu when the user clicks away from the toggle.
In a fresh vue project I started by making a basic toggle which shows or hides a menu:
To get the simple toggle menu above I changed App.vue
to this:
<template>
<div class="toggle-container">
<button class="toggle" @click="toggle = !toggle">Toggle</button>
<div v-if="toggle">
Menu
</div>
</div>
</template><script>
export default {
data () {
return {
toggle: false
}
},
}
</script><style scoped>
.toggle-container {
position: fixed;
top: 50%;
left: 50%;
}
</style>
This demonstrates the problem — ‘Clicking away’ from the toggle while the menu is open will not hide the menu again.
Solution
We can add an event listener to listen for a click event when the component is created, and one for when the component is unmounted:
created() {
window.addEventListener('click', this.close)
},
beforeUnmount() {
window.removeEventListener('click', this.close)
},
Then on the close event we can check to see if the toggle button has been clicked or outside by checking this.$el
(the root DOM element):
close(e) {
if (! this.$el.contains(e.target)) {
this.active = false
}
}
Combining these this is what my final App.vue
file looks like:
<template>
<div class="toggle-container">
<button class="toggle" @click="active = ! active">Toggle</button>
<div v-if="active">
Menu
</div>
</div>
</template><script>
export default {
data () {
return {
active: false
}
}, created() {
window.addEventListener('click', this.close)
},
beforeUnmount() {
window.removeEventListener('click', this.close)
}, methods: {
close(e) {
if (! this.$el.contains(e.target)) {
this.active = false
}
}
}
}
</script><style scoped>
.toggle-container {
position: fixed;
top: 50%;
left: 50%;
}
</style>
I personally like this solution because it is quite simple and avoids adding any extra dependencies. I hope it helps you in your project!