November 4, 2020

Tutorials

Vue Custom Directives – How to detect click outside

One of useful features of vue js that some of other front end frameworks lack are directives, with directives you can add functionalities to the template such as conditions, loops, data binding, etc.

While Vue has a handful of directives by default, you can create your own directives too. In this post I will create a custom directive named v-clickoutside to call a method whenever the user clicks outside the element that has this directive.

How to create a custom Vue directive

In order to register a global directive, you have to use the Vue.directive method, this function accepts two arguments, the first argument is the name of the directive in string and without v- prefix and the second argument is an object that contains element’s life cycle hooks which occur in this order:

bind: called when the directive is attached to the element.

inserted: called when the element is inserted into the parent DOM

update: called when the element is updated (but not it’s children)

componentUpdated: called when the component and It’s children have been updated

unbind: — called when the directive is removed

These hooks have these arguments:

el: the element that has the directive

binding: an object that contains the arguments that are passed to the hooks

vnode — virtual DOM node of the element

Click Outside directive

so which one of these hooks do we need for our click outside directive? we need to register the click and toch events when the element is inserted into DOM and unregister these events when the element is destroyed so we’ll use inserted and unbind hooks:

Vue.directive('clickoutside', {
  inserted: (el, binding, vnode) => {
// assign event to the element
    el.clickOutsideEvent = function (event) {
      // here we check if the click event is outside the element and it's children
      if (!(el == event.target || el.contains(event.target))) {
        // if clicked outside, call the provided method
        vnode.context[binding.expression](event)
      }
    }
// register click and touch events
    document.body.addEventListener('click', el.clickOutsideEvent)
    document.body.addEventListener('touchstart', el.clickOutsideEvent)
  },
  unbind: function (el) {
// unregister click and touch events before the element is unmounted
    document.body.removeEventListener('click', el.clickOutsideEvent)
    document.body.removeEventListener('touchstart', el.clickOutsideEvent)
  },
  stopProp(event) {
    event.stopPropagation()
  },
})

Usage

so how do we use this directive that we just created? for example we want to hide a div element when the user clicks outside that element:

<template>
  <div v-show="show" v-clickoutside="hide" class="hide-on-clickoutside">
    hide me when clicked outside
  </div>
</template>

<script>
export default {
    data(){
        return{
            show: true
        }
    },
    methods: {
        hide(){
            this.show = false
        }
    },
};
</script>

<style scoped></style>