Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Shivam Mishra
Shivam Mishra

Posted on • Originally published atshivam.dev

     

Detecting Clicks Outside an Element in Vue

You might have usedv-model,v-if,v-for orv-show shipped with Vue Core. These utilities are called directives, these are tiny commands that you can attach to DOM elements.

Vue Directives

When building apps, the primary form of code reuse and abstraction that Vue has to offer is components - however there may be cases where you may need some low-level DOM access on plain elements, and this is where custom directives should be used. It’s important to note that directives are meant to encapsulate DOM manipulations only, while components are self-contained units that have their own view and data logic.

One good example of such a use case is tooltips,v-tooltip is a popular library, it's a wrapper forpopperjs. This library can be registered as a directive and used as follows

<buttonv-tooltip="'You have ' + count + ' new messages.'">
Enter fullscreen modeExit fullscreen mode

A couple of days ago, I was working on the component system atdeepsource.io, building a dropdown menu component. I wanted the dropdown menu to close whenever we clicked outside the element. This is an ideal scenario where we could use a custom directive.

Building this as a directive will allow us to reuse this functionality wherever we want, for example modal component.

Writing Custom Directives

Vue provides us with a comprehensive suite of hooks that are triggered at specific stages of rendering the element. The hooks are as follows:

  • bind – This occurs once the directive is attached to the element. Think of this like aninit function
  • inserted – This hook occurs once the element is inserted into the parent DOM.
  • update – This hook is called when the element updates, but children haven’t been updated yet.
  • componentUpdated – This hook is called once the componentand the children have been updated.
  • unbind – This hook is called once the directive is removed.

Vue documentation has a good example av-focus directive withautofocus like behaviour for input components. You can check it outhere.

Let’s move to the directive we will build.

Outside Click Directive

Let’s start with a toy dropdown component

<template><buttonv-on:click="toggle"class="dropdown-button">Menu</button><divv-if="isOpen"v-outside-click="close"class="dropdown-body"><ul><li>Account Settings</li><li>Notifications</li><li>Log Out</li></ul></div></template><script>exportdefault{name:'ToyDropdown'data:{return{isOpen:false}},methods:{toggle(){this.isOpen=!this.isOpen},close(){this.isOpen=false}}}</script>
Enter fullscreen modeExit fullscreen mode

Here we want a function close to be triggered when clicked outside the element we’ve created the binding with Let’s start with a function that does exactly that.

functiononDocumentClick(e,el,fn){lettarget=e.target;if(el!==target&&!el.contains(target)){fn(e);}}
Enter fullscreen modeExit fullscreen mode

Let’s create the directive. In this case, we only need thebind andunbind hooks.

exportdefault{bind(el,binding){constfn=binding.value;constclick=function(e){onDocumentClick(e,el,fn);};document.addEventListener("click",click);},unbind(el){// Remove event handler},};
Enter fullscreen modeExit fullscreen mode

The bind, like other hooks receive a few arguments. You can see all of themhere. The one we are interested isbinding an object that contains the name of the directive, the value that is passed to it and more.

In our case, the value will be a function that will trigger on outside click.

This alone would work fine, however we need to remove the event listener onunbind this means we need to store the added event listener in the memory for reference later. This is simple to solve, all we need is an array, that we will store all the event listeners in. We will also attach an index to the data attributes of the element to recognise the index of the event listener.

Our directive function now looks like this

constinstance=[];exportdefault{bind(el,binding){// add the index to element data attributesel.dataset.outsideClickIndex=instances.length;constfn=binding.value;constclick=function(e){onDocumentClick(e,el,fn);};document.addEventListener("click",click);instances.push(click);},unbind(el){// Remove event handler},};
Enter fullscreen modeExit fullscreen mode

Now we can use theoutsideClickIndex andinstances array to remove the event listener on unbind

unbind(el){constindex=el.dataset.outsideClickIndex;consthandler=instances[index];document.removeEventListener('click',handler);instances.splice(index,1);}
Enter fullscreen modeExit fullscreen mode

Another enhancement we can do is to also add events fortouchstart

With this our directive looks something like this

letinstances=[];functiononDocumentClick(e,el,fn){lettarget=e.target;if(el!==target&&!el.contains(target)){fn(e);}}exportdefault{bind(el,binding){el.dataset.outsideClickIndex=instances.length;constfn=binding.value;constclick=function(e){onDocumentClick(e,el,fn);};document.addEventListener("click",click);document.addEventListener("touchstart",click);instances.push(click);},unbind(el){constindex=el.dataset.outsideClickIndex;consthandler=instances[index];document.removeEventListener("click",handler);document.removeEventListener("touchstart",click);instances.splice(index,1);},};
Enter fullscreen modeExit fullscreen mode

And here it is. In your main.js file you can register the directive as follows

importoutsideClickDirectivefrom"../../directives/outside-click";Vue.directive("outside-click",outsideClickDirective);
Enter fullscreen modeExit fullscreen mode

That’s all folks.

P.S. This post was originally published on my blog atshivam.dev.

References

  1. Custom Directives - Vue.js

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Front End engineering at DeepSource, focusing on Vue, Nuxt, TypeScript with a hint of GraphQL.
  • Location
    Mumbai, India
  • Education
    Bachelor of Engineering
  • Work
    Front End Engineer at DeepSource
  • Joined

More fromShivam Mishra

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp