How to prevent/stop propagation of default event (click) in directive (vue 2.x)

Vue.directive('login-to-click', {
  bind (el) {
    const clickHandler = (event) => {
      event.preventDefault()
      event.stopImmediatePropagation()
      alert('click')
    }
    el.addEventListener('click', clickHandler, true)
  }
})

usage

<button @click="handleClick" v-login-to-click>CLICK</button>

handleClick is always triggered. How I can prevent that from directive? Tried with/without addEventListener "capture" flag without any luck.


For now I ended up with following solution:

Vue.prototype.$checkAuth = function (handler, ...args) {
  const isLoggedIn = store.getters['session/isLoggedIn']
  if (isLoggedIn) {
    return handler.apply(this, args)
  } else {
    router.push('/login')
  }
}

And then in component

<button @click="$checkAuth(handleClick)">CLICK</button>

From my understanding those are two different event handlers, you are only preventing the default event of the one bound in the directive, this has no influence on @click however, because you are not overwriting the click listener but adding a second one. If you want the default of your @click binding to be prevented you can use @click.prevent="handleClick".

I don't think there's any way to do it from the directive, since you explicitly add another listener by binding @click to the button.

Event Handling — Vue.js, We can use the v-on directive to listen to DOM events and run some JavaScript var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // define the click event's propagation will be stopped --> <a v-on:click.stop="doThis"></a> <! to the browser that you don't want to prevent the event's default behavior. In this tutorial, you’ll learn how to easily stop mouse event propagation (DOM bubbling) in Vue.js. When you have a Vue.js method called from href tag you’ll experience DOM bubbling. It is a default browser behaviour. When you click on the <a> tag browser will navigate to the href. Consider this example:

in my app I have many buttons (follow/like/add to watchlist/block etc) that require user to be logged in to click on them

As with many things in Vue 2, this is a bad use case for a directive, but a very good use case for a component.

Here is a button that is only clickable when the user is authorized.

console.clear()

const AuthenticatedButton = {
  props:["onAuth", "onNonAuth", "disable", "auth"],
  template: `
    <button @click="onClick" 
            :disabled="disableWhenNotAuthorized">
      <slot>{{this.auth}}</slot>
    </button>`,
  computed:{
    disableWhenNotAuthorized(){
      return this.disable && !this.auth
    }
  },
  methods:{
    onClick(){
      if (this.auth && this.onAuth) this.onAuth()
      if (!this.auth && this.onNonAuth) this.onNonAuth()      
    }
  }
}

new Vue({
  el:"#app",
  data:{
    loggedIn: false
  },
  methods:{
    onClick(){
      alert("User is authenticated")
    },
    notAuthorized(){
      alert("You are not authorized.")
    }
  },
  components:{
    "auth-btn": AuthenticatedButton
  }
})
<script src="https://unpkg.com/vue@2.2.6/dist/vue.js"></script>
<div id="app">
  <h3>User is {{!loggedIn ? 'Not Authorized' : 'Authorized'}}</h3>
  <auth-btn :auth="loggedIn" 
            :on-auth="onClick"
            :on-non-auth="notAuthorized">
    Executes non auth handler when not authorized
  </auth-btn> <br>
  <auth-btn :auth="loggedIn" 
            :on-auth="onClick"
            :disable="true">
    Disabled when not authorized
  </auth-btn> <br><br>
  <button @click="loggedIn = true">Authenicate User</button>
</div>

Add a preventDefault event filter � Issue #1369 � vuejs/vue � GitHub, I find it pretty cumbersome to add an event parameter and write e. I guess it makes sense only if the element has the v-on directive on it! I can easily imagine wanting multiple things to happen on a click. Like will stop propagation, will prevent default --> <button yyx990803 closed this on Oct 2, 2015. To @MladenDanic's point, prevent will prevent default behavior, and prevent.stop will also stop propagation. For example: If you are triggering a custom method from an anchor click in a dropdown navigation, prevent.stop will also prevent the 'click' event, and leave the dropdown open.

https://vuejs.org/v2/guide/events.html#Event-Modifiers

There are a couple modifiers. .prevent is what I was looking for

<a href="#" @click.prevent="sendMessage('just one click');">

Vue Directive & Event Modifiers, modifiers and event modifiers in Vue.js, namely the stop, prevent and once modifiers. This is because the click event for the button element propagates up the We can disable this default behavior by using an event modifier that If we try again, we'll see that only one alert is shown this time, which is� is there an event decorator I can add to the child element. because I do want event bubbling for every child element except this one other button. {{UPDATE: i used @click.prevent.stop for desired effect! }} – Akin Hwan Jun 7 '18 at 20:12

You can use:

event.target.preventDefault();

I am not sure why would want to do that though, since you are adding a click listener for a reason.

Perhaps you could clarify your question a bit more.

Vue - Event Handling, When we want to listen to DOM events we can use the v-on directive el: '# example-2', data: { name: 'Vue.js' }, // defines the method under the Here the click event's propagation will be stopped --> <a v-on:click.stop="doThis"></a> <! chaining modifiers--> <a v-on:click.stop.prevent="doThat"></a> <! <button v-on:click.stop="showDialog">Click Here!</button> Notice the syntax; I simply added a dot followed by the name of the modifier, after the name of the event. If we try again, we’ll see that only one alert is shown this time, which is the one triggered by the click event on the button.

V-outside-click: Writing Vue Directives For Seamless Interactions , +Register directives with Vue.directive('directive-name', directiveObject) +V- outside-click triggers an event if an element is clicked outside of Although we can do this easily inside methods, it would be better if the methods can be purely about data logic rather than having to deal with DOM event details. To address this problem, Vue.js provides two event modifiers for v-on: .prevent and .stop. Recall that modifiers are directive postfixes denoted by a dot:

How to correctly use preventDefault(), stopPropagation(), or return , preventDefault() , stopPropagation(), and return false; are not interchangeable, nor are they tools of trial-and-error. We'll start off with a pretty common UI pattern — a file upload panel A fileUpload function to trigger the click event on the file upload input This prevents the default behaviour of an element. 🔲 Vue directive to react on clicks outside an element without stopping the event propagation - ndelvalle/v-click-outside

A tiny Vue directive that stop propagation scroll when edge reached, A tiny Vue directive that stop propagation scroll when edge reached : white_check_mark: On, :x: Off By default directive works on both direction but you can strict it by via holding the mouse button ("drag and drop" or "click and hold" style). A collection of handy unit conversion filters for use in Vue 2. The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases. It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed. If you want to stop those behaviors, see the preventDefault() method.

Comments
  • My use case is very simple: in my app I have many buttons (follow/like/add to watchlist/block etc) that require user to be logged in to click on them. I wanted to add v-login-to-click directive that would prevent any clicks on given element/component if user is not logged in (Instead I would redirect user to login page or show login modal).
  • Well like I said, you can't overwrite the clicklistener that you bind with @click from a directive. The only thing you can do is use click.prevent everywhere you use both the directive and @click or (beware: hacky) use the el reference in the directive and remove all other click listeners. I don't really think 2) is an option because it'll literally remove the handleClick in your case.
  • This is a good example, but suppose that one of the components is not a button, but a link or image. Then I would have to prepare sich wrapper for each of them.
  • @user606521 This literally took me about 10 minutes to write. Everything except the template could be a mixin if needed. Your concern is valid, but feels like premature optimization.
  • For now I ended up with another solution (please see my edits in question). I just want to keep code clean and as small as possible - I am working on a project with 100+ different pages. I am not sure if my solution is not little bit "hacky" but it works well.
  • @user606521 Aboslutely, go with what works best for you :) I primarily wanted to point out the main vector for re-use in Vue 2 is components. Another way to handle this might be higher order components which is essentially a component that wraps any other component returning a new one. In this case, an "authorized" HOC could potentially be applied to any other component. This is something much more common in React but is making headway in Vue.
  • event.target.preventDefault is undefined.
  • So what does your event actually show when you log it?