Events: v-on and modifiers
The v-on directive attaches an event handler — code that runs on a click, a key press or a form submission. It replaces addEventListener from the tutorial. The short form is usually used — @ followed by the event name.
A handler function
<!-- src/App.vue -->
<script setup>
import { ref } from 'vue'
const todos = ref([
{ id: 1, text: 'Buy bread', done: false },
{ id: 2, text: 'Call Anna', done: true },
])
function clearDone() {
todos.value = todos.value.filter((todo) => !todo.done)
}
</script>
<template>
<p>Tasks: {{ todos.length }}</p>
<button @click="clearDone">Delete finished</button>
</template>The page shows "Tasks: 2", after the click — "Tasks: 1". @click gets the function name without parentheses: Vue calls it on click.
A handler with an argument
If a function needs data, call it with parentheses right in the template:
<script setup>
import { ref } from 'vue'
const todos = ref([
{ id: 1, text: 'Buy bread', done: false },
{ id: 2, text: 'Call Anna', done: false },
])
function toggle(todo) {
todo.done = !todo.done
}
</script>
<template>
<ul>
<li v-for="todo in todos" :key="todo.id" @click="toggle(todo)">
{{ todo.done ? '✓' : '○' }} {{ todo.text }}
</li>
</ul>
</template>Clicking a task toggles the mark at the start of its row. For simple actions a function is optional: @click="todo.done = !todo.done" works the same.
The event object
If a handler is given without parentheses, Vue passes it the event object — the same event as in addEventListener. With parentheses, the object is available through the special $event variable:
<script setup>
function logKey(event) {
console.log('Key pressed:', event.key)
}
</script>
<template>
<input @keydown="logKey" placeholder="Press a key">
</template>When you press keys in the field, the console prints their names: Key pressed: a, Key pressed: Enter.
Modifiers
A modifier is written after the event name with a dot and saves you from boilerplate in the handler:
| Modifier | What it does |
|---|---|
.prevent | Calls event.preventDefault() |
.stop | Calls event.stopPropagation() |
.once | The handler fires only once |
.self | Only if the event happened on the element itself |
.enter, .esc | Only for the given key |
<template>
<form @submit.prevent="addTodo">
<input @keyup.esc="clearInput">
<button>Add</button>
</form>
</template>.prevent stops the form from reloading the page on submit, and .esc calls clearInput only when Escape is pressed. The addTodo and clearInput functions are declared in <script setup>.
Common mistake
In the template both @click="clearDone" and @click="clearDone()" work. The mistake is different: writing @click="toggle" for a function that needs an argument. It then receives the event object instead of the task, and todo.done turns out to be undefined. If a function needs data, pass it explicitly: @click="toggle(todo)".