2

I am trying to create a custom directive using this example: https://stackoverflow.com/a/42389266/5470563

main.ts file:

import Vue from 'vue';
import HeaderNav from './components/HeaderNav.vue'

Vue.directive('click-outside', {
    bind: function (el, binding, vnode) {
        el.clickOutsideEvent = function (event) {
            // here I check that click was outside the el and his childrens
            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)
    },
});

new Vue({
    el: '#app',
        components: {
            HeaderNav
        }
});

And I get a compile error:

Property 'clickOutsideEvent' does not exist on type 'HTMLElement'.

How do I fix it? Or is there any better solution to bind events on outside element click?

The50
  • 1,096
  • 2
  • 23
  • 47

2 Answers2

2

You have 2 options

cast el param in bind, unbind function to any.

bind: function (el: any, binding, vnode) {
unbind: function (el: any) {

extend HTMLElement interface.

You can place it before directive declaration.

interface HTMLElement {
  clickOutsideEvent: () => void;
}
Józef Podlecki
  • 10,453
  • 5
  • 24
  • 50
  • Compile runs fine now, but I get JS error after clicking anywhere: Uncaught TypeError: vnode.context[binding.expression] is not a function at HTMLBodyElement.el.clickOutsideEvent – The50 May 11 '20 at 14:22
  • Oh it's because I had no function defined as attribute for v-click-outside custom directive. Thank you! – The50 May 11 '20 at 14:24
1

Here is an implementation I currently use, hope it helps:

export default {
  bind: function(el, binding, vNode) {
    // Provided expression must evaluate to a function.
    if (typeof binding.value !== "function") {
      const compName = vNode.context.name;
      let warn = `[Vue-click-outside:] provided expression '${binding.expression}' is not a function, but has to be`;
      if (compName) {
        warn += `Found in component '${compName}'`;
      }

      console.warn(warn);
    }
    // Define Handler and cache it on the element
    const bubble = binding.modifiers.bubble;
    const handler = e => {
      if (bubble || (!el.contains(e.target) && el !== e.target)) {
        binding.value(e);
      }
    };
    el.__vueClickOutside__ = handler;
    // add Event Listeners
    document.addEventListener("mousedown", handler);
  },
  unbind: function(el, binding) {
    // Remove Event Listeners
    document.removeEventListener("mousedown", el.__vueClickOutside__);
    el.__vueClickOutside__ = null;
  }
};

Then register the directive in your main file

// Directives
Vue.directive("click-outside", ClickOutside);