Explain Codes LogoExplain Codes Logo

Detect click outside element

javascript
event-listeners
vue-directives
reactive-programming
Anton ShumikhinbyAnton ShumikhinΒ·Feb 4, 2025
⚑TLDR

Click outside detection requires attention at the document level. Test if the event's target is outside your element of interest by listening for clicks on the document itself. Here is the distilled essence in JavaScript:

// Grab your popcorn 🍿 and your element const elem = document.getElementById('elem'); // The show begins! πŸŽ₯ document.addEventListener('click', (e) => { if (!elem.contains(e.target)) { // Ta-da! Clicked outside. Now, make some noise! πŸŽ‰ console.log('Clicked outside elem!'); } });

Listen globally, test with .contains(), react if outside.

Vue.js: A reactive approach

Traditional JavaScript solutions work well, but in Vue.js applications, you need a more reactive and declarative solution. Here comes Vue custom directives to the rescue! They allow for great reusability across components.

Vue 2: Creating a custom directive

Let's create a custom directive using bind and unbind lifecycle hooks to set up and clean up the event listeners on the element:

Vue.directive('click-outside', { bind: function (el, binding, vnode) { // Ninja code ahead πŸ” el.clickOutsideEvent = function (event) { // Checking with .contains(), just like a detective πŸ•΅οΈβ€β™€οΈ if (!(el == event.target || el.contains(event.target))) { vnode.context[binding.expression](event); } }; document.body.addEventListener('click', el.clickOutsideEvent) }, unbind: function (el) { // Cleaning up after the party 🧹 document.body.removeEventListener('click', el.clickOutsideEvent) }, });

And to wield this directive in your template:

<template> <div v-click-outside="outsideClickHandler"> <!-- Component contents: Your HTML masterpiece 🎨 --> </div> </template>

Vue 3: Handling lifecycle changes

The arrival of Vue 3 gives us the Composition API, altering how we register directives. Replace bind and unbind with beforeMount and unmounted lifecycle hooks respectively:

const clickOutsideDirective = { beforeMount(el, binding) { // Same as Vue 2 `bind`, but more elegant πŸ’ƒ }, unmounted(el) { // Same as Vue 2 `unbind`, but more courteous 🎩 } }; app.directive('click-outside', clickOutsideDirective);

Exploring focus-based events

Another strategy involves @focus and @focusout combined with tabindex, offering a lighter solution for click outside detection in specific cases.

Third-party solutions to fast track

For the time crunched, vue-clickaway, v-click-outside, or vue3-click-outside are well-maintained packages that can give you a head start, ensuring cross-browser compatibility and preventing reinvention of the wheel.

Remember to clean up!

Regardless of the chosen approach, always remove event listeners when done to prevent potential memory leaks, a courtesy especially crucial in single-page applications.

Tricky scenarios and solutions

Event delegation with dynamic elements

In scenarios where the content is dynamic or manipulated by the user, event delegation ensures the listeners adapt to the dynamic changes in the DOM.

Wise use of event propagation

Care should be exercised when using event.stopPropagation(). While it can prevent triggering click events on parents, using it indiscriminately can lead to sneaky bugs.

Nested interactive elements

Beware of cases involving nested interactive elements where you wouldn't want the 'outside' click if a descendant of the focused target element is clicked.

Touch devices and keyboard accessibility

Finally, don't forget to make your click outside detection compatible with touch events in addition to regular clicks. Preserve keyboard accessibility for screen-reader users and those who navigate with the keyboard.