Vue.js allows us to intercept any DOM event by using the v-on directive on an element.

If we want to do something when a click event happens in this element:

<template>
  <a>Click me!</a>
</template>

we add a v-on directive:

<template>
  <a v-on:click="handleClick">Click me!</a>
</template>

Vue also offers a very convenient alternative syntax for this:

<template>
  <a @click="handleClick">Click me!</a>
</template>

You can choose to use the parentheses or not. @click="handleClick" is equivalent to @click="handleClick()".

handleClick is a method attached to the component:

<script>
export default {
  methods: {
    handleClick: function(event) {
      console.log(event)
    }
  }
}
</script>

Methods are explained more in detail in my Vue Methods tutorial.

What you need to know here is that you can pass parameters from events: @click="handleClick(param)" and they will be received inside the method.

Access the original event object

In many cases, you will want to perform an action on the event object or look up some property in it. How can you access it?

Use the special $event directive:

<template>
  <a @click="handleClick($event)">Click me!</a>
</template>

<script>
export default {
  methods: {
    handleClick: function(event) {
      console.log(event)
    }
  }
}
</script>

and if you already pass a variable:

<template>
  <a @click="handleClick('something', $event)">Click me!</a>
</template>

<script>
export default {
  methods: {
    handleClick: function(text, event) {
      console.log(text)
      console.log(event)
    }
  }
}
</script>

From there you could call event.preventDefault(), but there’s a better way: event modifiers

Event modifiers

Instead of messing with DOM “things” in your methods, tell Vue to handle things for you:

  • @click.prevent call event.preventDefault()
  • @click.stop call event.stopPropagation()
  • @click.passive makes use of the passive option of addEventListener
  • @click.capture uses event capturing instead of event bubbling
  • @click.self make sure the click event was not bubbled from a child event, but directly happened on that element
  • @click.once the event will only be triggered exactly once

All those options can be combined by appending on modifier after the other.

For more on propagation, bubbling/capturing see my JavaScript events guide.


Components in Vue can communicate in various ways.

Props

The first way is using props.

Parents “pass down” data by adding arguments to the component declaration:

<template>
  <div>
    <Car color="green" />
  </div>
</template>

<script>
import Car from './components/Car'

export default {
  name: 'App',
  components: {
    Car
  }
}
</script>

Props are one-way: from parent to child. Any time the parent changes the prop, the new value is sent to the child and rerendered.

The reverse is not true, and you should never mutate a prop inside the child component.

Events to communicate from children to parent

Events allow you to communicate from the children up to the parent:

<script>
export default {
  name: 'Car',
  methods: {
    handleClick: function() {
      this.$emit('clickedSomething')
    }
  }
}
</script>

The parent can intercept this using the v-on directive when including the component in its template:

<template>
  <div>
    <Car v-on:clickedSomething="handleClickInParent" />
    <!-- or -->
    <Car @clickedSomething="handleClickInParent" />
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    handleClickInParent: function() {
      //...
    }
  }
}
</script>

You can pass parameters of course:

<script>
export default {
  name: 'Car',
  methods: {
    handleClick: function() {
      this.$emit('clickedSomething', param1, param2)
    }
  }
}
</script>

and retrieve them from the parent:

<template>
  <div>
    <Car v-on:clickedSomething="handleClickInParent" />
    <!-- or -->
    <Car @clickedSomething="handleClickInParent" />
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    handleClickInParent: function(param1, param2) {
      //...
    }
  }
}
</script>