Vue — close menu when user clicks away

Paul Reaney
2 min readFeb 21, 2021

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!

--

--

Paul Reaney

I am a software developer and I like to write about interesting things I come across in my day to day.