Vue Clicked Outside: Creating Custom Listener for Clicking Outside a Component

Saidur Rahman Riad
HTML Beginners
Published in
3 min readMar 13, 2022

--

Listening for a Click event outside a component is a very common task for any web apps. It gives more dynamic look to the web app and is great for a better user experience.
There are a few npm packages that solves this problem but with a little understanding this can be done easily. Learning how to create reusable components helps mastering the process fairly easily. In this tutorial we will create a component that will handle clicked outside event and make it completely modular thus ensuring that it can be used in any Vue app.

Creating the Template

First create a new component, for this lets name it ClickedOutsideComponent.vue.

We need to use ref in vue to create a reference to the parent element. The parent element will hold a slot where we can pass other element or component for which we will be listening for the element.

<template>
<div ref="clickOutsideRef">
<slot></slot>
</div>
</template>

And with this we are done with the template. Now we have to write the logic for handling clicked outside event.

The Driving Force That Made It Possible

We will use vue composition api. The logic is to listen for mousedown event and check if the element is under our clicked outside component. This event need to be run once the component is mounted. TypeScript for this will look like this:

<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue'
export default defineComponent({
setup(props) {
const clickOutsideRef = ref(null)
const handler = (event: Event) => {
// handle the main logic here
}
onMounted(() => {
document.addEventListener('mousedown', handler)
})
onUnmounted(() => {
document.removeEventListener('mousedown', handler)
})
return {
clickOutsideRef
}
},
})
</script>

Here we are adding an event listener for mousedown event once the component is mounted and removing it once the component is unmounted. The handler function will take care of the logic.

Handling the Logic

To make sure the component is completely reusable we will emit an event called clickedOutside to listen from the parent component. To check if the mousedown event if fired outside our component we just need to find if the clicked element is under our ClickedOutsideElement. The code will look like this:

const handler = (event: Event) => {
const clickedOutsideElement = clickOutsideRef.value as unknown as HTMLElement
const clickedNode = event.target as Node

if(!clickedOutsideElement.children[0].contains(clickedNode)) emit('clickedOutside')
}

Here we check if the clicked element is a child of clicked outside element. If not then we emit a custom event.

And with a little bit of code we are done with our clicked outside component.

The Whole of It

The final code of this will be:

<template>
<div
ref="clickOutsideRef"
>
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue'
export default defineComponent({
setup(props, { emit }) {
const clickOutsideRef = ref(null)
const handler = (event: Event) => {
const clickedOutsideElement = clickOutsideRef.value as unknown as HTMLElement
const clickedNode = event.target as Node

if(!clickedOutsideElement.children[0].contains(clickedNode)) emit('clickedOutside')
}
onMounted(() => {
document.addEventListener('mousedown', handler)
})
onUnmounted(() => {
document.removeEventListener('mousedown', handler)
})
return {
clickOutsideRef
}
},
})
</script>

This component receives a single element into a slot and emits clickedOutsideevent if mouse is clicked outside this element.

But How Do We Use It

A simple use case example for this component:

<ClickedOutsideComponent @clickedOutside="handleEvent" />
<div>
<h1>Hello World</h1>
</div>
</ClickedOutsideComponent>

Full code can be found here on my GitHub. Feel free to drop a star if it helps you.

Happy Coding.

--

--