11. Listening to DOM events and Event modifiers

We have been kind of on a dry spell on the events front. Let’s learn how to listen to DOM events,click events in specific, today. Take a look at the starting code,

Index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Hello Vue!</title>
    <!-- including Vue with development version CDN -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h2>Welcome</h2>
      <button>Say Hello</button>
      <p> {{ message }} </p>
    </div>
    <!-- including index.js file -->
    <script src="index.js"></script>
  </body>
</html>

Index.js

var app = new Vue({
  el: "#app",
  data: {
    message: "Hi"
  }
});

The most famous button event on Earth is the click event. Let us say, we want to change the message, Hi to Hello, when the button ‘Say Hello’ is clicked. Any idea how we can achieve this with Vue?

Brace yourselves, people, Vue has another directive for just this purpose, v-on! We can always use v-on to listen to DOM events such as,

  • Mouse events – click, dblclick, mousemove, mouseover etc.
  • Keyboard events – keyup, keydown, keypress etc.
  • Form events – submit, focus, blur etc.
  • And many more

Listening to click event

So, our task now is to listen when a click event happens on the button, ‘Say Hello’ and change the message text from ‘Hi’ to ‘Hello’. One thing is clear. We have to add our v-on directive to the button element that is where we will be listening to our click event.

<button v-on:click="message='Hello'">Say Hello</button>

Dissecting the syntax,

  • v-on – Vue understands that we are listening to an event by using this directive
  • click – specify the type of event we are listening to after a colon
  • “” – after an equals symbol, specify the method name or the JavaScript code that we would like to execute when the event is triggered.

We are accessing the message property from the Vue instance’s data object and setting it to “Hello”. The output is as follows,

click event

Cool! But in real applications, we would want to do so much more than just changing the text of a property.

Brain tugging question: How can we fit complicated JavaScript code within the double quotes of the v-on directive?

Using methods

The answer to the above brain tugging question is to use methods. Just like the “data” object, our Vue instance has an optional “methods” object where we can define our all methods.

Now, within the double quotes of v-on, just mention the name of the method and pass required arguments (if any). Then, define the method in the “methods” object of the Vue instance which will be triggered every time a click event happens.

Index.html (snippet)

<button v-on:click="greet('howdy')">Say Hello</button>

Index.js

var app = new Vue({
  el: "#app",
  data: {
    message: "Hi"
  },
  // define all the methods within the 'methods' object
  methods: {
    greet(greeting) {
      // 'this' keyword refers to the current Vue instance
      this.message = greeting;
    }
  }
});

Did you notice the usage of this keyword? In order to refer to the data object’s properties from the HTML, we can use them directly because we hook a particular HTML section to the Vue instance with the ‘el’ keyword. But, inside methods, we have to point to the Vue instance using ‘this’ keyword and then access the properties of the data object.

click event with method

Clicking on the button, ‘Say Hello’ triggered the greet(‘howdy’) method and the code within the method is executed, rendering ‘howdy’ to the webpage.

Event modifiers

Imagine we have a button named, ‘Add 1’ and a counter whose initial value is zero. Every time the button is clicked, it adds 1 to the existing counter value and prints it to the screen.

Let us bring our imagination to reality now. Just use the v-on directive with click as the argument that triggers a method which adds 1 to the counter value.

Index.html (snippet)

<button v-on:click="addOne">Add 1</button>
<p> {{ counter }} </p>

Index.js (snippet)

methods: {
  addOne: function() {
    this.counter += 1;
  }
}

Brain tugging question: What if we want to trigger this method only on the first click and never again?

The answer to this question is – event modifiers! These allow us to modify the behavior of the event (just as the name implies).

Usage: Following the event name, click in our case, add a dot and specify the name of the modifier to be used.

<button v-on:click.once="addOne">Add 1</button>

With the event modifier .once, the click event will be triggered only once. This means that the “addOne” method will be called only once incrementing the value of the counter to 1 irrespective of the number of times the button is clicked.

.once event modifier

Similarly, the other event modifiers available are,

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

The most commonly used modifiers are .stop and .prevent which call the well-known event.stopPropagation() and event.preventDefault() methods. These are the methods that come with the native Event Object. Nothing to do with Vue here. Vue.js just provides an easy way do deal with such common event details with the help of modifiers instead of specifying these event methods (say, event.preventDefault() to cancel the event) explicitly inside the Vue instance’s methods.

Quick refresher for the two most commonly used modifiers:

The .stop modifier would stop the propagation of the event any further, as per the stopPropagation() event method.

The .prevent modifier would prevent the default action from occurring, as per the preventDefault() method. Best example for .prevent is to use it with Submit button so that when submit event is triggered, it will not reload the page.

Chaining the modifiers

Warning: This can be a little mind-bending in the beginning!

Vue lets us to chain these modifiers one after another like so,

v-on:click.self.prevent

The order of chaining is very important. Because the code related to each modifier is generated in the same order.

For example,

<a href="https://vuejs.org/" v-on:click.self.prevent target="_blank">Open Vue
  <p>Click me now</p>
</a>

Using v-on:click.self.prevent will only prevent clicks on the <a> element itself and not its child element. Okay I hear you say “English please”! In other words, it means,

  • clicking on “Open Vue” of <a> tag will prevent the vuejs.org page from opening
  • clicking on “Click me now” of <p> tag will open vuejs.org official page in a new tab

Instead, if we change the order of the chained modifiers to v-on:click.prevent.self, all click events will be prevented from happening.

<a href="https://vuejs.org/" v-on:click.prevent.self target="_blank">Open Vue
  <p>Click me now</p>
</a>

Clicking on both “Open Vue” of <a> and “Click me now” of <p> will not open vuejs.org (or in other words, it is prevented from opening).

Let’s now take a look at the complete code we dealt with so far,

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Hello Vue!</title>
    <!-- including Vue with development version CDN -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h2>Welcome</h2>
      <!-- Listening to click event with v-on directive -->
      <button v-on:click="greet('howdy')">Say Hello</button>
      <p> {{ message }} </p>
      <h2>Event Modifiers</h2>
      <!-- Using .once to modify click event -->
      <button v-on:click.once="addOne">Add 1</button>
      <p> {{ counter }} </p>
      <!-- chaining event modifiers -->
      <a href="https://vuejs.org/" v-on:click.prevent.self target="_blank">Open Vue
        <p>Click me now</p>
      </a>
    </div>
    <!-- including index.js file -->
    <script src="index.js"></script>
  </body>
</html>

index.js

var app = new Vue({
  el: "#app",
  data: {
    message: "Hi",
    counter: 0
  },
  // define all the methods within the 'methods' object
  methods: {
    greet(greeting) {
      // 'this' keyword refers to the current Vue instance
      this.message = greeting;
    },
    // another way to define methods before es6
    addOne: function() {
      this.counter += 1;
    }
  }
});

I know there is a lot to digest today. All the code discussed above along with self-explanatory comments is available in the GitHub repo. Take your time and I will be back soon to discuss keyboard and other mouse DOM events.

Have a nice day ahead!

2 Comments

  1. Hi Chandana,
    Just to confirm, Where there is no argument in method brackets are not required while invoking the method. That is the reason you have written like this.
    v-on:click=”addOne” // Here only name of the method is used and no bracket

    Also why you have used function where there is no argument like this
    addOne: function() {
    this.counter += 1;
    }
    Can’t you write like this
    addOne() {
    this.counter += 1;
    }

    • Hi Soniya,
      Thanks for your query. Yes, you can definitely write the function as,
      addOne() {
      this.counter += 1;
      }
      I just wanted to show how this same function was written before ES6. That is the reason, I coded it as,
      addOne: function() {
      this.counter += 1;
      }
      I have mentioned the same with the help of a comment above this function, “// another way to define methods before es6” in the GitHub repo. Anyways, I will add the final complete code in this article too.

      In short, both work just the same 🙂

      Keep learning,
      Chandana

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.