Détecter cliquez à l'extérieur de l'élément

Comment puis-je détecter un clic en dehors de mon élément? Je suis l'aide de Vue.js donc ça va être en dehors de mon élément de gabarits. Je sais comment le faire dans Vanilla JS, mais je ne suis pas sûr qu'il y ait une meilleure façon de le faire, quand j'utilise Vue.js?

C'est la solution pour vanille JS: JavaScript détecter événement en dehors de div

je suppose que je peux utiliser une meilleure façon d'accéder à l'élément?

41
demandé sur Community 2016-03-23 08:24:55

10 réponses

peut être résolu correctement en configurant une seule directive personnalisée:

Vue.directive('click-outside', {
  bind () {
      this.event = event => this.vm.$emit(this.expression, event)
      this.el.addEventListener('click', this.stopProp)
      document.body.addEventListener('click', this.event)
  },   
  unbind() {
    this.el.removeEventListener('click', this.stopProp)
    document.body.removeEventListener('click', this.event)
  },

  stopProp(event) { event.stopPropagation() }
})

Utilisation:

<div v-click-outside="nameOfCustomEventToCall">
  Some content
</div>

dans le composant:

events: {
  nameOfCustomEventToCall: function (event) {
    // do something - probably hide the dropdown menu / modal etc.
  }
}

démo de travail sur JSFiddle avec des informations supplémentaires sur les mises en garde:

https://jsfiddle.net/Linusborg/yzm8t8jq /

38
répondu Linus Borg 2018-04-23 08:20:01

il y a la solution que j'ai utilisée, qui se base sur la réponse de Linus Borg et fonctionne très bien avec vue.js 2.0

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)
  },
});

Il y a un petit démo

vous pouvez trouver plus d'informations sur les directives personnalisées et ce que el, binding, vnode signifie dans https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments

72
répondu MadisonTrash 2018-07-18 11:29:49

Il y a deux paquets disponibles dans la communauté pour cette tâche (les deux sont maintenus):

8
répondu Julien Le Coupanec 2017-09-09 23:35:17
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('click', handler)
  },

  unbind: function (el, binding) {
    // Remove Event Listeners
    document.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null

  }
}
4
répondu xiaoyu2er 2018-06-06 08:00:35

ça a marché pour moi avec Vue.js 2.5.2:

/**
 * Call a function when a click is detected outside of the
 * current DOM node ( AND its children )
 *
 * Example :
 *
 * <template>
 *   <div v-click-outside="onClickOutside">Hello</div>
 * </template>
 *
 * <script>
 * import clickOutside from '../../../../directives/clickOutside'
 * export default {
 *   directives: {
 *     clickOutside
 *   },
 *   data () {
 *     return {
         showDatePicker: false
 *     }
 *   },
 *   methods: {
 *     onClickOutside (event) {
 *       this.showDatePicker = false
 *     }
 *   }
 * }
 * </script>
 */
export default {
  bind: function (el, binding, vNode) {
    el.__vueClickOutside__ = event => {
      if (!el.contains(event.target)) {
        // call method provided in v-click-outside value
        vNode.context[binding.expression](event)
        event.stopPropagation()
      }
    }
    document.body.addEventListener('click', el.__vueClickOutside__)
  },
  unbind: function (el, binding, vNode) {
    // Remove Event Listeners
    document.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null
  }
}
2
répondu nyl_auster 2018-01-03 07:50:10

vous pouvez enregistrer deux écouteurs d'événements pour click event comme ceci

document.getElementById("some-area")
        .addEventListener("click", function(e){
        alert("You clicked on the area!");
        e.stopPropagation();// this will stop propagation of this event to upper level
     }
);

document.body.addEventListener("click", 
   function(e) {
           alert("You clicked outside the area!");
         }
);
1
répondu saravanakumar 2016-03-23 05:48:07

j'utilise ce code:

"bouton" tag

 <a @click="visualSwitch()"> show hide </a>

Afficher/masquer la balise:

<div class="dialog-popup" v-if="visualState" @click.stop=""></div>

variable d'état

data () { return {
    visualState: false,
}},
methods: {
    visualSwitch() {
        document.removeEventListener('click', this.visualSwitch);
        this.visualState = !this.visualState;
    },
},

regarder

watch: {
    visualState() {
        if (this.visualState)
            document.addEventListener('click', this.visualSwitch);
    }
}
1
répondu Pax Exterminatus 2018-05-17 18:50:33

j'ai une solution pour la manipulation À Bascule menu déroulant:

export default {
data() {
  return {
    dropdownOpen: false,
  }
},
methods: {
      showDropdown() {
        console.log('clicked...')
        this.dropdownOpen = !this.dropdownOpen
        // this will control show or hide the menu
        $(document).one('click.status', (e)=> {
          this.dropdownOpen = false
        })
      },
}
0
répondu Nicolas S.Xu 2017-06-27 21:22:16

j'ai mis à jour la réponse de MadisonTrash pour prendre en charge le Safari Mobile (qui n'a pas" événement 151910920", touchend doit être utilisé à la place). Cela inclut également une vérification afin que l'événement ne soit pas déclenché par glisser sur les appareils mobiles.

Vue.directive('click-outside', {
    bind: function (el, binding, vnode) {
        el.eventSetDrag = function () {
            el.setAttribute('data-dragging', 'yes');
        }
        el.eventClearDrag = function () {
            el.removeAttribute('data-dragging');
        }
        el.eventOnClick = function (event) {
            var dragging = el.getAttribute('data-dragging');
            // Check that the click was outside the el and its children, and wasn't a drag
            if (!(el == event.target || el.contains(event.target)) && !dragging) {
                // call method provided in attribute value
                vnode.context[binding.expression](event);
            }
        };
        document.addEventListener('touchstart', el.eventClearDrag);
        document.addEventListener('touchmove', el.eventSetDrag);
        document.addEventListener('click', el.eventOnClick);
        document.addEventListener('touchend', el.eventOnClick);
    }, unbind: function (el) {
        document.removeEventListener('touchstart', el.eventClearDrag);
        document.removeEventListener('touchmove', el.eventSetDrag);
        document.removeEventListener('click', el.eventOnClick);
        document.removeEventListener('touchend', el.eventOnClick);
        el.removeAttribute('data-dragging');
    },
});
0
répondu benrwb 2018-05-22 10:34:30

les gens veulent souvent savoir si l'utilisateur quitte le composant root (fonctionne avec tous les composants de niveau)

Vue({
  data: {},
  methods: {
    unfocused : function() {
      alert('good bye');
    }
  }
})
<template>
  <div tabindex="1" @blur="unfocused">Content inside</div>
</template>
-1
répondu Denis Danilenko 2017-03-13 14:40:13