100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
JavaScript

Event Handling with v-on

Explains how Vue's v-on directive (shorthand @) wires DOM and custom component events to handlers, including inline expressions, method handlers, and modifiers.

Template DirectivesBeginner7 min readJul 9, 2026
Analogies

Event Handling with v-on

v-on, almost always written with its @ shorthand, is Vue's directive for listening to DOM events and, on components, custom emitted events. It accepts either an inline JavaScript expression evaluated in the template's scope, or the name of a component method to call as a handler, receiving the native or custom event object as its first argument automatically when needed. Because v-on hooks directly into the underlying addEventListener mechanics, it supports the full range of native DOM events (click, input, submit, keydown, and so on) as well as any custom event name a child component emits via defineEmits.

🏏

Cricket analogy: v-on is like an umpire's earpiece that listens for both standard signals — a bowler's appeal, a batsman's leave — and custom team signals a fielder invents, hooking directly into the same underlying communication channel either way.

Inline expressions vs method handlers

For very short logic, an inline expression like @click="count++" is idiomatic and avoids the ceremony of defining a separate function. For anything beyond a one-liner, Vue expects a method handler — a function reference like @click="handleSubmit" — which Vue automatically passes the native event object to as the first parameter. If you need to pass the event alongside custom arguments, you call the function explicitly in the template and reference the special $event variable, e.g. @click="handleSubmit($event, item.id)", so the native event isn't lost when you add extra arguments.

🏏

Cricket analogy: An inline handler like @click="runs++" is idiomatic for a quick scoreboard tap, but for anything bigger you use a named method like @click="recordWicket", which Vue automatically passes the event to; to also pass the batsman's id you call it explicitly with $event.

Event modifiers

Vue provides event modifiers — dot-suffixed keywords appended to the event name — that handle common patterns declaratively instead of doing them imperatively inside the handler. .stop calls event.stopPropagation(), .prevent calls event.preventDefault(), .self only triggers the handler if the event originated on the element itself (not a child), .once removes the listener after it fires once, and .capture uses the capture phase instead of bubbling. Modifiers can be chained, and their order matters — @click.prevent.stop prevents default first, then stops propagation, applied in the order written. There are also key modifiers for keyboard events, like @keyup.enter or @keydown.esc, which filter the handler to only run for that specific key.

🏏

Cricket analogy: Event modifiers are like an umpire's shorthand signals: .stop is waving off a runner before the throw goes further, .prevent is calling dead ball before play continues, .once is a one-time no-ball warning, and .self only counts an appeal from the bowler himself, not a teammate.

vue
<script setup>
import { ref } from 'vue'

const email = ref('')
const submitted = ref(false)

function handleSubmit(event) {
  submitted.value = true
  console.log('Form submitted with', email.value)
}

function removeItem(event, itemId) {
  console.log('Removing item', itemId, 'triggered by', event.type)
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="email" type="email" @keyup.enter="handleSubmit" />
    <button type="submit">Submit</button>
  </form>

  <button @click="removeItem($event, 42)">Remove item 42</button>
</template>

Vue's event modifiers exist because raw event-handling boilerplate (event.preventDefault(), event.stopPropagation()) is extremely common and easy to forget. React has no template-level equivalent — you always call event.preventDefault() manually inside the handler function. Vue's declarative modifiers move that intent into the template itself, making the handler's purpose easier to scan at a glance.

On a custom component, @click="handler" does NOT automatically listen to the child's root element's native click event unless the child explicitly forwards it — actually, by default Vue does pass non-emitted native listeners through to the component's root element via 'attribute fallthrough', but if the child component declares its own click in defineEmits, or has multiple root nodes, that fallthrough behavior can be disabled or become ambiguous. Always check whether the event you're binding on a custom component is a native DOM event falling through, or a custom event explicitly emitted via defineEmits — they resolve differently and mixing up the two is a common source of 'my click handler never fires' bugs.

  • v-on (shorthand @) binds both native DOM events and custom component-emitted events to handler expressions or methods.
  • Method handlers automatically receive the native event object as the first argument; use $event to pass it explicitly alongside custom arguments.
  • Modifiers like .stop, .prevent, .self, .once, and .capture replace common imperative event-object calls with declarative template syntax.
  • Modifier chaining order matters — modifiers are applied in the order they're written.
  • Key modifiers (e.g. .enter, .esc) restrict a keyboard handler to fire only for a specific key.
  • On custom components, native listeners fall through to the root element by default, but this can be affected by multiple root nodes or explicit emits declarations.

Practice what you learned

Was this page helpful?

Topics covered

#JavaScript#VueJsStudyNotes#WebDevelopment#EventHandlingWithVOn#Event#Handling#Inline#Expressions#StudyNotes#SkillVeris