vue.jsvuejs3vue-directives

Migrating "detect click outside" custom directive from Vue 2 to Vue 3


Based on this question Detect click outside element and this answer https://stackoverflow.com/a/42389266, I'm trying to migrate the directive from Vue 2 to Vue 3. It seems that binding.expression and vnode.context not exists more. How can I make it work?

app.directive('click-outside', {
    beforeMount (el, binding, vnode) {
        el.clickOutsideEvent = function (event) {
            if (!(el === event.target || el.contains(event.target))) {
                vnode.context[binding.expression](event);
            }
        };
        document.body.addEventListener('click', el.clickOutsideEvent);
    },
    unmounted (el) {
        document.body.removeEventListener('click', el.clickOutsideEvent);
    }
});

Solution

  • You can use binding.value instead like this:

    const { createApp } = Vue;
    
    const highlightEl = (color ) => (event, el) => {
      if (el) {
        el.style.background = color;
      } else {
        event.target.style.background = color;
      }
    }
    const clearHighlightEl = (event, el) => {
      if (el) {
        el.style.background = '';
      } else {
        event.target.style.background = '';
      }
    }
    
    const app = Vue.createApp({
      setup() {
        return {
          highlightEl,
          clearHighlightEl
        }
      }
    })
    
    app.directive('click-outside', {
      mounted(el, binding, vnode) {
        el.clickOutsideEvent = function(event) {
          if (!(el === event.target || el.contains(event.target))) {
            binding.value(event, el);
          }
        };
        document.body.addEventListener('click', el.clickOutsideEvent);
      },
      unmounted(el) {
        document.body.removeEventListener('click', el.clickOutsideEvent);
      }
    });
    
    app.mount('#app')
    <script src="https://unpkg.com/vue@3.0.0-rc.11/dist/vue.global.prod.js"></script>
    
    <div id="app">
      <h1 v-click-outside="highlightEl('yellow')" @click="clearHighlightEl">Element 1</h1>
      <p v-click-outside="highlightEl('#FFCC77')" @click="clearHighlightEl">Element 2</p>
    </div>