DEV Community

Cover image for  Handling Events with Vue.js
Anthony Gore
Anthony Gore

Posted on • Edited on

Handling Events with Vue.js

When you build a dynamic website with Vue you'll most likely want it to be able to respond to events.

For example, if a user clicks a button, submits a form, or even just moves their mouse, you may want your Vue site to respond somehow.

Note: this article was originally posted here on the Vue.js Developers blog on 2020/01/06.

Handling events with Vue

We can intercept an event by adding the v-on directive to the relevant DOM element. Let's say we want to handle a click on a button element - we can add the following to our Vue template:

<button v-on:click="clickHandler"></button>

Note that we add an argument to the v-on directive, which will be the name of the event we want to handle (in this case, click).

We then bind an expression to the directive, which will normally be a method you want to use to handle the event. In this case, we've called it clickHandler.

Tip: the v-on directive has a convenient shorthand @ that can be used instead of v-on: like this: <button @click="clickHandler"></button>.

Types of events you can handle

Besides click, what other DOM events can be handled? Vue can handle any kind of web or mobile native event (as well as custom events which we'll talk about later) including:

  • submit
  • keyup
  • drag
  • scroll

And more. Here's a list of the most common DOM events for your reference.

Event handling methods

If we bind a method to our event handling directive we can now run some custom code.

Let's keep it simple in this example and just log a message to the console, but you could also do something more interesting like show/hide another element, increment a counter, etc.

<div id="app">
  <button v-on:click="clickHanlder"></button>
</div>
new Vue({
  el: "#app",
  methods: {
    clickHandler () {
      console.log("You clicked the button!");
    }
  }
})

Event object

An event object gets passed to your event handler which opens more possibilities for how you can respond to an event. This object contains a lot of useful properties and methods including a reference to the element where the event originated (event.target), the time the event occurred (event.timeStamp) and more.

clickHandler (event) {
  console.log(`The button was clicked at ${event.timeStamp}.`);
}

Note that this object is provided by the native Web API, not Vue, so it will be the same object you'd find in pure JavaScript. Here's the event interface reference for your convenience.

Event modifiers

A common pattern in JavaScript apps is to handle a form submit manually rather than using the native functionality. To do this, you need to use the native preventDefault method of the submit event before running your form handling code, otherwise the page will be redirected before it has a chance to complete.

formHandler (event) {
  event.preventDefault();
  // form handling logic
}

Rather than doing this manually in your handler, Vue offers an event modifier to do this directly from the template. Note the modifier is added after the . in the directive:

<form @submit.prevent="formHandler"></form>

Vue offers several different event modifiers that are useful in common event handling scenarios:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

Custom events

So far we've been talking about handling native events. But Vue is a component-based framework, so could we make a component emit its own event?

Yes, and this can be very useful. Let's say want a child component to send data up to a parent component. We can't use props here, as prop data only goes from parent to child, and not the other way.

ParentComponent
 |
 | (data travels down via props, never up)  
 v 
ChildComponent

The solution is to have the child component emit an event, and have the parent listen to it.

To do this, call this.$emit("my-event") from the child component when you want the event to be emitted. For example, say we have a component DialogComponent that needs to inform it's parent MainPage that it has been closed:

DialogComponent

export default {
  methods: {
    onClose() {
      this.$emit("dialog-closed");
    }
  }
};

The parent component can then handle the custom event exactly the same as it would a native event.

MainPage

<div>
  <dialog-component @dialog-closed="eventHandler" />
</div>

You can also send data in your custom event which can be received in the handling method:

DialogComponent

onClose() {
  this.$emit("dialog-closed", { time: Date.now() });
}

MainPage

eventHandler (event, { time }) {
  console.log(`Dialog was closed at ${time}`);
}

Tip: use kebab-case names for your custom events! HTML is case-insensitive, so a camel-case event name e.g. myEvent will, confusingly, be myevent in the template. So it's best to use kebab-case e.g. my-event and avoid confusion.

Event bus

As we've seen above, a child component can send an event to a parent component. But what if you want a component to send an event to any other component in the hierarchy? For example, a sibling, grand-parent, etc.

To achieve this we can use a pattern called event bus. This is where we create a separate instance of Vue to transport an event to any component that imports it.

First, create and export a Vue instance in a new module file:

eventBus.js

import Vue from "vue";
export default new Vue();

Next, import the bus into a component where you would like to emit an event. You can use the $emit method of you bus Vue instance.

import eventBus from "./eventBus";

export default {
  ...
  methods: {
    myMethod () {
      eventBus.$emit("my-event")
    }
  }
}

Finally, import the bus into a component where you would like to listen to the event. You should then set up a listener somewhere in your code. I suggest you use a lifecycle hook like created as you can access the component instance here.

This is done with the $on method of the bus which takes two arguments - the event you want to listen for, and a callback.

import eventBus from "./eventBus";

export default {
  ...
  created () {
    eventBus.$on("my-event", () => {
      console.log("my-event called on global event bus");
    });
  }
}

And there you have it - a way to share data between any components in your app!

Tip: a more scalable solution for passing data around an app is to use Vuex.


Enjoy this article?

Get more articles like this in your inbox weekly with the Vue.js Developers Newsletter.

Click here to join!


Top comments (0)