Reputation:
How can I detect a click outside my element? I'm using Vue.js so it's gonna be outside my templates element. I know how to do it in Vanilla JS, but I'm not sure if there's a more proper way to do it, when I'm using Vue.js?
This is the solution for Vanilla JS: Javascript Detect Click event outside of div
I guess I can use a better way to access the element?
Upvotes: 216
Views: 312035
Reputation: 17388
Add a supplement to @Julien Le Coupanec's answer.
If feasible, consider incorporating a widely-used package, such as https://vueuse.org/core/onClickOutside/.
However, it's important to be aware of the drawbacks associated with utilizing a niche npm package, which include:
Upvotes: 0
Reputation: 6576
This answer is based on MadisonTrash's great answer above but updated to use new Vue 3 syntax.
Vue 3 now uses beforeMount
instead of bind
, and unmounted
instead of unbind
(src).
const clickOutside = {
beforeMount: (el, binding) => {
el.clickOutsideEvent = event => {
// here I check that click was outside the el and his children
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call method provided in attribute value
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted: el => {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
createApp(App)
.directive("click-outside", clickOutside)
.mount("#app");
You bind to it using v-click-outside
:
<div v-click-outside="doStuff"></div>
Upvotes: 57
Reputation: 7988
There are three packages available in the community for this task (all are maintained):
Upvotes: 35
Reputation: 5751
If you're using vue 3
with script setup
, you can do this:
<script setup>
import {ref} from 'vue'
const elementRef = ref(null)
window.addEventListener('click', (event) => {
if (!elementRef.value.contains(event.target)){
console.log('click outside element')
}
})
</script>
<template>
<div ref="elementRef">your element</div>
</template>
or if you don't want to use event listeners at all, here's an example where the child closes if you click on the parent while not hovering the child:
<script setup>
import { reactive } from 'vue'
const state = reactive({
show: false,
hover: null
})
const closeModal = () => {
if (!state.hover) {
state.show = false
}
}
</script>
<div class="parent w-20 h-20 bg-gray-400" v-if="state.show" @click=closeModal>
<div class="child w-10 h-10 bg-red-400" @mouseenter=state.hover = true @mouseleave=state.hover = false>hello world</div>
</div>
Upvotes: 5
Reputation: 1862
I created mixin for Vue 2:
Mixin file:
export const detectClickOutside = {
data() {
return {
statusElement: false,
nameElement: undefined,
ref: undefined,
};
},
watch: {
statusElement(isShow) {
if (isShow) document.addEventListener('mousedown', this.handleClickOutside);
else this.stopEventListener();
},
},
beforeDestroy() {
this.stopEventListener();
},
methods: {
stopEventListener() {
this.statusElement = false;
this.nameElement = undefined;
this.ref = undefined;
document.removeEventListener('mousedown', this.handleClickOutside);
},
setStatusShowingElement(status, name, refElement) {
this.statusElement = status;
this.nameElement = name;
this.ref = refElement;
},
handleClickOutside({ target }) {
if (this.ref && !this.ref.contains(target)) this[this.nameElement] = false;
},
},
};
And how to use it in component:
<button @click="openModal = !openModal">Click</button> // button for open modal
component which will be open:
<ExampleComponent v-show="openModal" ref="modal" />
and watcher to send data to mixin:
watch: {
openModal(newValue) {
this.setStatusShowingElement(
newValue,
'openModal',
this.$refs.modal.$el,
);
},
},
Upvotes: 0
Reputation: 14108
https://jsbin.com/yomimudahi/edit?html,css,js,output
document.addEventListener("click", printMousePos);
function printMousePos(event) {
const list = document.getElementsByTagName("nav")[0];
var ssdd = list.classList.contains("test2");
if (ssdd === false && (event.clientX > list.clientWidth || event.clientY > list.clientHeight)) {
list.classList.add("test2");
}
}
function openNav() {
const list = document.getElementsByTagName("nav")[0];
list.classList.remove("test2");
}
@media only screen and (min-width: 701px) {
main {
display: grid;
width: 100%;
grid-template-areas: "nav main";
grid-template-columns: 150px 1fr;
position: relative;
}
main > nav {
grid-area: nav;
background-color: #ffa08c;
}
main > section {
grid-area: main;
background-color: #ffff64;
}
main > section > aside {
grid-area: head;
background-color: #8ca0ff;
}
.test {
display: none;
}
}
@media only screen and (max-width: 700px) {
main {
display: grid;
width: 100%;
grid-template-areas: "main";
grid-template-columns: 1fr;
position: relative;
}
main > nav {
height: 300px;
background-color: coral;
position: fixed;
}
main > section {
grid-area: main;
background-color: #ffff64;
}
main > section > aside {
grid-area: head;
background-color: #8ca0ff;
}
.test2 {
display: none;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<main>
<nav class="test2">
<header>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTCPQ5XLyAjzq-NHitpSKVIsF8LorhPxewY_vERnurZLA&s" width="50" height="50" />
</header>
<div>
Navigation
</div>
</nav>
<section>
<aside>
Aside
<div class="test" onclick="openNav()">
<img src="https://cdn-icons-png.flaticon.com/512/1752/1752735.png" width="50" height="50" />
</div>
</aside>
<article>
Content<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
</article>
</section>
</main>
</body>
</html>
Upvotes: 0
Reputation:
You can achieve it by using few lines of script. I am using nuxt3 for this but I think it should also work on vue3
const showDropdown = ref( false )
onMounted( () => {
window.addEventListener( 'click', ( e ) => {
if ( document.getElementById( 'dropdown' ).contains( e.target ) ) {
showDropdown.value = !showDropdown.value
}
else {
showDropdown.value = false
}
} )
} )
In my template.
<div>
<div
class="flex gap-2 items-center relative px-5"
id="dropdown"
>
<div class="w-5 h-5">
<IconsCog />
</div>
<div class="relative">Settings</div>
<div
v-if="showDropdown"
class="absolute bg-white w-full top-[40px] left-0 shadow-md rounded"
>
<ul>
<li class="px-4 text-sm py-3">Account</li>
<li class="px-4 text-sm pb-3">Logout</li>
</ul>
</div>
</div>
</div>
Stackblitz example https://stackblitz.com/edit/nuxt-starter-cnn3qc?file=app.vue
Upvotes: 2
Reputation: 1988
In addition to all the great answers, I just want to mention that you can also use VueUse composable onClickOutside.
Here is an example usage as a directive
<template>
<component v-if="showDialog" v-on-click-outside="() =>showDialog = false"/>
</template>
<script>
import { vOnClickOutside } from '@vueuse/components'
const showDialog = ref(true)
</script>
You can also use it directly as a component method or as a renderless component.
Upvotes: 0
Reputation: 4470
Vue 3 has breaking changes in directives, all of <Vue3 methods were changed/updated. If you wonder, how to do it in Vue 3
, Here's the snippet. For information please go through this link
<div v-click-outside="methodToInvoke"></div>
click-outside.js
export default {
beforeMount: function (el, binding, vnode) {
binding.event = function (event) {
if (!(el === event.target || el.contains(event.target))) {
if (binding.value instanceof Function) {
binding.value(event)
}
}
}
document.body.addEventListener('click', binding.event)
},
unmounted: function (el, binding, vnode) {
document.body.removeEventListener('click', binding.event)
}
}
and In main.js
add the following
// Directives
import ClickOutside from './click-outside'
createApp(App)
.directive('click-outside', ClickOutside)
.use(IfAnyModules)
.mount('#app')
Upvotes: 8
Reputation: 64312
If anyone would like to accomplish this using Vue 3 composables, you can use this. If you'd like to code it yourself, see below:
event.js
import { onMounted, onUnmounted } from 'vue';
export function useEventListener(target, event, callback) {
onMounted(() => target.addEventListener(event, callback));
onUnmounted(() => target.removeEventListener(event, callback));
}
click-outside.js
import { unref } from 'vue';
import { useEventListener } from './event';
export function useClickOutside(ref, cb) {
let target = null;
const listener = (event) => {
if (!target) target = unref(ref);
if (!(target === event.target || target.contains(event.target))) cb();
};
useEventListener(document.body, 'click', listener);
}
Your Component
<script setup>
import { ref } from 'vue';
import { useClickOutside } from 'path/to/click-outside';
const myCallback = () => {
console.log('You clicked outside the container!');
};
const container = ref(null);
useClickOutside(container, myCallback);
</script>
<template>
<div ref="container"></div>
</template>
Note that useEventListener
is a common pattern and can be used independent of useClickOutside
.
Upvotes: 0
Reputation: 2827
Despite having so many answers under this thread, I still struggled to get the full implementation of the click outside event. You can follow the steps below to see the complete implementation process.
main.js -- register a global directive (just copy and paste)
import Vue from 'vue';
Vue.directive('click-outside', {
bind(el, binding) {
const handler = (e) => {
if ((!el.contains(e.target) && el !== e.target)) {
binding.value(e);
}
}
el.__vueClickOutside__ = handler;
document.addEventListener('click', handler);
},
unbind(el) {
document.removeEventListener('click', el.__vueClickOutside__);
el.__vueClickOutside__ = null;
}
});
YourComponent.vue -- the use of the directive
<template>
<div>
<button @click.stop="showCalloutMenu()">Callout</button>
<section v-if="callout" v-click-outside="hideCalloutMenu">
Your callout menu content
</section>
</div>
<script>
export default {
data:() => ({
callout: false,
}),
methods: {
showCalloutMenu() {
this.callout = true;
// or if you prefer toggle effect, use
// this.callout = !this.callout;
},
hideCalloutMenu() {
this.callout = false;
},
}
};
Caveats
Make sure to use .stop
event modifier on your callout element, otherwise your callout menu will not show up.
@click.stop="showCalloutMenu()"
Upvotes: 0
Reputation: 151
Just if anyone is looking how to hide modal when clicking outside the modal. Since modal usually has its wrapper with class of modal-wrap
or anything you named it, you can put @click="closeModal"
on the wrapper. Using event handling stated in vuejs documentation, you can check if the clicked target is either on the wrapper or on the modal.
methods: {
closeModal(e) {
this.event = function(event) {
if (event.target.className == 'modal-wrap') {
// close modal here
this.$store.commit("catalog/hideModal");
document.body.removeEventListener("click", this.event);
}
}.bind(this);
document.body.addEventListener("click", this.event);
},
}
<div class="modal-wrap" @click="closeModal">
<div class="modal">
...
</div>
<div>
Upvotes: 1
Reputation: 81
The short answer: This should be done with Custom Directives.
There are a lot of great answers here that also say this, but most of the answers I have seen break down when you start using outside-click extensively (especially layered or with multiple excludes). I have written an article on medium talking about the nuances of Custom Directives and specifically implementation of this one. It may not cover all edge cases but it has covered everything I have thought up.
This will account for multiple bindings, multiple levels of other element exclusions and allow your handler to only manage the "business logic".
Here's the code for at least the definition portion of it, check out the article for full explanation.
var handleOutsideClick={}
const OutsideClick = {
// this directive is run on the bind and unbind hooks
bind (el, binding, vnode) {
// Define the function to be called on click, filter the excludes and call the handler
handleOutsideClick[el.id] = e => {
e.stopPropagation()
// extract the handler and exclude from the binding value
const { handler, exclude } = binding.value
// set variable to keep track of if the clicked element is in the exclude list
let clickedOnExcludedEl = false
// if the target element has no classes, it won't be in the exclude list skip the check
if (e.target._prevClass !== undefined) {
// for each exclude name check if it matches any of the target element's classes
for (const className of exclude) {
clickedOnExcludedEl = e.target._prevClass.includes(className)
if (clickedOnExcludedEl) {
break // once we have found one match, stop looking
}
}
}
// don't call the handler if our directive element contains the target element
// or if the element was in the exclude list
if (!(el.contains(e.target) || clickedOnExcludedEl)) {
handler()
}
}
// Register our outsideClick handler on the click/touchstart listeners
document.addEventListener('click', handleOutsideClick[el.id])
document.addEventListener('touchstart', handleOutsideClick[el.id])
document.onkeydown = e => {
//this is an option but may not work right with multiple handlers
if (e.keyCode === 27) {
// TODO: there are minor issues when escape is clicked right after open keeping the old target
handleOutsideClick[el.id](e)
}
}
},
unbind () {
// If the element that has v-outside-click is removed, unbind it from listeners
document.removeEventListener('click', handleOutsideClick[el.id])
document.removeEventListener('touchstart', handleOutsideClick[el.id])
document.onkeydown = null //Note that this may not work with multiple listeners
}
}
export default OutsideClick
Upvotes: 3
Reputation: 5614
There is the solution I used, which is based on Linus Borg answer and works fine with vue.js 2.0.
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
// here I check that click was outside the el and his children
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call method provided in attribute value
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
});
You bind to it using v-click-outside
:
<div v-click-outside="doStuff">
You can find some more info about custom directives and what el, binding, vnode means in https://v2.vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments
Upvotes: 267
Reputation: 5762
A lot of these answers seem to be over-complicating this, which is making me wonder if I'm missing something. But the solution seems simple: just compare this.$el
(or one of its children) to e.target
. No non-Vue event listeners needed — just the Vue @click
listener.
In this example, I have a Vue component that renders a modal. I want to close the modal if someone clicks on the .modal
element (which is the overlay behind the modal), but not if they click on any of its children (i.e., the contents of the modal). Here's the code to do that:
// MyComponent.js
export default {
methods: {
handleClickModal(e) {
// Prevent clicks on child elements (e.g., `.modal-dialog`) from
// closing the modal.
if (this.$el === e.target) {
closeMyModal()
}
},
},
template: `
<div class="modal" style="display:initial" @click="handleClickModal">
<div class="modal-dialog">
<!-- modal content here... -->
</div>
</div>
`,
}
It Just Works™.
Upvotes: 0
Reputation: 596
For those using Vue 3.
Vue3 has changed the syntax for Directive Hooks:
To detect a click outside an element in Vue 3:
click-outside.js
export default function directive(app) {
// you can name the directive whatever you want. -> click-outside
app.directive('click-outside', {
beforeMount(el, binding) {
el.clickOutsideEvent = (evt) => {
evt.stopPropagation();
if (!(el === evt.target || el.contains(evt.target))) {
binding.value(evt, el);
}
};
window.requestAnimationFrame(() => {
document.addEventListener("click", el.clickOutsideEvent);
});
},
unmounted(el) {
document.removeEventListener("click", el.clickOutsideEvent);
},
})
}
Register directive:
main.js
import { createApp } from "vue";
import App from "./App.vue";
// Import your directive, in order to register it.
import clickOutside from "./directives/click-outside.js"
createApp(App).use(clickOutside).mount("#app");
Usage:
<template>
<div class="dropdown" v-click-outside="() => hideDropdown()"></div>
</template>
<script setup>
function hideDropdown() {
console.log("close dropdown")
}
</script>
### OR
<script>
methods: {
hideDropdown() {
console.log("close dropdown")
}
}
</script>
Upvotes: 4
Reputation: 29
Frequently people want to know if user leave root component (works with any level components)
Vue({
data: {},
methods: {
unfocused : function() {
alert('good bye');
}
}
})
<template>
<div tabindex="1" @blur="unfocused">Content inside</div>
</template>
Upvotes: 0
Reputation: 311
I create a div at the end of the body like that:
<div v-if="isPopup" class="outside" v-on:click="away()"></div>
Where .outside is :
.outside {
width: 100vw;
height: 100vh;
position: fixed;
top: 0px;
left: 0px;
}
And away() is a method in Vue instance :
away() {
this.isPopup = false;
}
Upvotes: 6
Reputation: 5168
I did it a slightly different way using a function within created().
created() {
window.addEventListener('click', (e) => {
if (!this.$el.contains(e.target)){
this.showMobileNav = false
}
})
},
This way, if someone clicks outside of the element, then in my case, the mobile nav is hidden.
Upvotes: 19
Reputation: 699
Now you should use the vue-click-outside plugin for this.
you can run an event when you click outside that's div.
NPM Plugin: https://www.npmjs.com/package/v-click-outside
Upvotes: -1
Reputation: 144
This Worked for me with Vue.js 2.5.2 :
/**
* Call a function when a click is detected outside of the
* current DOM node ( AND its children )
*
* Example :
*
* <template>
* <div v-click-outside="onClickOutside">Hello</div>
* </template>
*
* <script>
* import clickOutside from '../../../../directives/clickOutside'
* export default {
* directives: {
* clickOutside
* },
* data () {
* return {
showDatePicker: false
* }
* },
* methods: {
* onClickOutside (event) {
* this.showDatePicker = false
* }
* }
* }
* </script>
*/
export default {
bind: function (el, binding, vNode) {
el.__vueClickOutside__ = event => {
if (!el.contains(event.target)) {
// call method provided in v-click-outside value
vNode.context[binding.expression](event)
event.stopPropagation()
}
}
document.body.addEventListener('click', el.__vueClickOutside__)
},
unbind: function (el, binding, vNode) {
// Remove Event Listeners
document.body.removeEventListener('click', el.__vueClickOutside__)
el.__vueClickOutside__ = null
}
}
Upvotes: 10
Reputation: 23968
Keep in attention that this solution only works with Vue 1.
Can be solved nicely by setting up a custom directive once:
Vue.directive('click-outside', {
bind () {
this.event = event => this.vm.$emit(this.expression, event)
this.el.addEventListener('click', this.stopProp)
document.body.addEventListener('click', this.event)
},
unbind() {
this.el.removeEventListener('click', this.stopProp)
document.body.removeEventListener('click', this.event)
},
stopProp(event) { event.stopPropagation() }
})
Usage:
<div v-click-outside="nameOfCustomEventToCall">
Some content
</div>
In the component:
events: {
nameOfCustomEventToCall: function (event) {
// do something - probably hide the dropdown menu / modal etc.
}
}
Working Demo on JSFiddle with additional info about caveats:
https://jsfiddle.net/Linusborg/yzm8t8jq/
Upvotes: 141
Reputation: 11
I'm not sure if someone will ever see this answer but here it is. The idea here is to simply detect if any click was done outside the element itself.
I first start by giving an id to the main div of my "dropdown".
<template>
<div class="relative" id="dropdown">
<div @click="openDropdown = !openDropdown" class="cursor-pointer">
<slot name="trigger" />
</div>
<div
class="absolute mt-2 w-48 origin-top-right right-0 text-red bg-tertiary text-sm text-black"
v-show="openDropdown"
@click="openDropdown = false"
>
<slot name="content" />
</div>
</div>
</template>
And then I just loop thru the path of the mouse event and see if the div with my id "dropdown" is there. If it is, then we good, if it is no, then we close the dropdown.
<script>
export default {
data() {
return {
openDropdown: false,
};
},
created() {
document.addEventListener("click", (e) => {
let me = false;
for (let index = 0; index < e.path.length; index++) {
const element = e.path[index];
if (element.id == "dropdown") {
me = true;
return;
}
}
if (!me) this.openDropdown = false;
});
}
};
</script>
I'm pretty sure this can bring performance issues if you have many nested elements, but I found this as the most lazy-friendly way of doing it.
Upvotes: 0
Reputation: 397
This is a complete solution based on MadisonTrash answer, and benrwb and fredrivett tweaks for safari compatibility and vue 3 api changes.
The solution proposed below is still useful, and the how to use is still valid, but I changed it to use document.elementsFromPoint
instead of event.contains
because it doesn't recognise as children some elements like the <path>
tags inside svgs. So the right directive is this one:
export default {
beforeMount: (el, binding) => {
el.eventSetDrag = () => {
el.setAttribute("data-dragging", "yes");
};
el.eventClearDrag = () => {
el.removeAttribute("data-dragging");
};
el.eventOnClick = event => {
const dragging = el.getAttribute("data-dragging");
// Check that the click was outside the el and its children, and wasn't a drag
console.log(document.elementsFromPoint(event.clientX, event.clientY))
if (!document.elementsFromPoint(event.clientX, event.clientY).includes(el) && !dragging) {
// call method provided in attribute value
binding.value(event);
}
};
document.addEventListener("touchstart", el.eventClearDrag);
document.addEventListener("touchmove", el.eventSetDrag);
document.addEventListener("click", el.eventOnClick);
document.addEventListener("touchend", el.eventOnClick);
},
unmounted: el => {
document.removeEventListener("touchstart", el.eventClearDrag);
document.removeEventListener("touchmove", el.eventSetDrag);
document.removeEventListener("click", el.eventOnClick);
document.removeEventListener("touchend", el.eventOnClick);
el.removeAttribute("data-dragging");
},
};
const clickOutside = {
beforeMount: (el, binding) => {
el.eventSetDrag = () => {
el.setAttribute("data-dragging", "yes");
};
el.eventClearDrag = () => {
el.removeAttribute("data-dragging");
};
el.eventOnClick = event => {
const dragging = el.getAttribute("data-dragging");
// Check that the click was outside the el and its children, and wasn't a drag
if (!(el == event.target || el.contains(event.target)) && !dragging) {
// call method provided in attribute value
binding.value(event);
}
};
document.addEventListener("touchstart", el.eventClearDrag);
document.addEventListener("touchmove", el.eventSetDrag);
document.addEventListener("click", el.eventOnClick);
document.addEventListener("touchend", el.eventOnClick);
},
unmounted: el => {
document.removeEventListener("touchstart", el.eventClearDrag);
document.removeEventListener("touchmove", el.eventSetDrag);
document.removeEventListener("click", el.eventOnClick);
document.removeEventListener("touchend", el.eventOnClick);
el.removeAttribute("data-dragging");
},
}
createApp(App)
.directive("click-outside", clickOutside)
.mount("#app");
This solution watch the element and the element's children of the component where the directive is applied to check if the event.target
element is also a child. If that's the case it will not trigger, because it's inside the component.
You only have to use as any directive, with a method reference to handle the trigger:
<template>
<div v-click-outside="myMethod">
<div class="handle" @click="doAnotherThing($event)">
<div>Any content</div>
</div>
</div>
</template>
Upvotes: 9
Reputation: 2534
There are already many answers to this question, and most of them are based on the similar custom directive idea. The problem with this approach is that one have to pass a method function to the directive, and cannot directly write code as in other events.
I created a new package vue-on-clickout
that is different. Check it out at:
It allows one to write v-on:clickout
just like any other events. For example, you can write
<div v-on:clickout="myField=value" v-on:click="myField=otherValue">...</div>
and it works.
vue-on-clickout
now supports Vue 3!
vue-on-clickout
is now replaced by a new package Clickout-Event
which works for any front-end framework (or vanilla)!
Upvotes: 5
Reputation: 175
If you're specifically looking for a click outside the element but still within the parent, you can use
<div class="parent" @click.self="onParentClick">
<div class="child"></div>
</div>
I use this for modals.
Upvotes: 13
Reputation: 21
You can create new component which handle outside click
Vue.component('click-outside', {
created: function () {
document.body.addEventListener('click', (e) => {
if (!this.$el.contains(e.target)) {
this.$emit('clickOutside');
})
},
template: `
<template>
<div>
<slot/>
</div>
</template>
`
})
And use this component:
<template>
<click-outside @clickOutside="console.log('Click outside Worked!')">
<div> Your code...</div>
</click-outside>
</template>
Upvotes: 0
Reputation: 4703
I hate additional functions so... here is an awesome vue solution without an additional vue methods, only var
<p @click="popup = !popup" v-out="popup">
<div v-if="popup">
My awesome popup
</div>
data:{
popup: false,
}
Vue.directive('out', {
bind: function (el, binding, vNode) {
const handler = (e) => {
if (!el.contains(e.target) && el !== e.target) {
//and here is you toggle var. thats it
vNode.context[binding.expression] = false
}
}
el.out = handler
document.addEventListener('click', handler)
},
unbind: function (el, binding) {
document.removeEventListener('click', el.out)
el.out = null
}
})
Upvotes: 4
Reputation: 808
<button
class="dropdown"
@click.prevent="toggle"
ref="toggle"
:class="{'is-active': isActiveEl}"
>
Click me
</button>
data() {
return {
isActiveEl: false
}
},
created() {
window.addEventListener('click', this.close);
},
beforeDestroy() {
window.removeEventListener('click', this.close);
},
methods: {
toggle: function() {
this.isActiveEl = !this.isActiveEl;
},
close(e) {
if (!this.$refs.toggle.contains(e.target)) {
this.isActiveEl = false;
}
},
},
Upvotes: 2