codejump

AcademyVue

Event Handling: v-on and Modifiers

Passing a handler versus calling it in the template is the same pitfall React has with onClick, and modifiers replace the boilerplate everyone hand-writes for it.

2 min readUpdated

3 exercises

Waiting at the end of this page.

A bare method call runs at render, not at the event

v-on:click="fn" (shorthand @click="fn") passes the function itself, to be called when the event fires. Writing @click="fn()" calls it immediately, during template evaluation, and passes whatever it returns — usually undefined — as the handler instead:

vue
<template>
  <!-- Bug: addTodo() runs once, on render, not on click -->
  <button @click="addTodo()">Add</button>

  <!-- Correct: the reference is passed -->
  <button @click="addTodo">Add</button>

  <!-- Correct, and the only way to pass an argument -->
  <button @click="() => removeTodo(index)">Remove</button>
</template>

The rule is the same one a React codebase enforces on onClick: pass a reference for a no-argument call, wrap in an arrow function the moment you need to pass something the handler does not receive from the event itself.

Modifiers replace the boilerplate every handler used to write by hand

Before modifiers existed, every form handler opened with the same two lines:

JavaScript
function handleSubmit(event) {
  event.preventDefault()
  // ...
}

.prevent moves that call out of the handler and onto the directive itself:

vue
<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="draft" />
    <button type="submit">Add</button>
  </form>
</template>

Without it, pressing Enter in the input — or clicking a type="submit" button — reloads the page, because the browser's default form behavior runs before Vue's handler gets a chance to stop it by hand.

.stop is the same idea for event.stopPropagation(), and it matters most when a clickable child sits inside a clickable parent:

vue
<template>
  <li @click="openItem(index)">
    {{ todo.text }}
    <!-- Without .stop, deleting also fires the <li>'s own click handler,
         opening the item at the exact moment it is removed -->
    <button @click.stop="removeTodo(index)">✕</button>
  </li>
</template>

.once removes the listener after it fires once, and key modifiers (@keyup.enter, @keydown.esc) skip the if (event.key === 'Enter') check every keyboard handler used to open with. All four compose: @click.stop.prevent runs both in the order written.

What interviewers check

Whether you can explain why @click="fn()" is wrong without running it first — it is the single most common Vue template bug, and naming it correctly (a function call versus a function reference) signals you understand what a template attribute actually evaluates to, not just how to copy the syntax that happens to work.

Now practice it

2 quizzes · 1 drill

Reading this page is the cheap half. These are the exercises that make you use it.