Skip to content

Events ​

A .vue template binds a listener the usual way — @click, @mousedown, and so on. Every listener receives one shared event object: type (the event's name), target/currentTarget, the propagation methods below, and whatever fields that event carries.

target and currentTarget are both the id of the node currently receiving the event — they're always equal, since there's no way yet to learn which descendant an event actually started on (no event delegation).

vue
<template>
  <div @mousedown="onMouseDown">Click and hold</div>
</template>

NOTE

These are the only event names wired to real input. Binding any other name is accepted but never fires.

Pointer ​

click ​

Fires on a mousedown followed by a mouseup on the same node, before the mouseup listeners run — or after Enter/Space while the node is focused. Carries no fields of its own.

mousedown / mouseup / mousemove ​

A subset of the DOM's MouseEvent's fields:

  • clientX / clientY — pointer position
  • pageX / pageY — identical to clientX/clientY; nothing here scrolls the page itself, which is the only thing that would tell them apart
  • movementX / movementY — delta from whichever mouse/wheel event fired last; 0 for the first one. A mousemove and the mouseenter/mouseleave from the same pointer move report the same delta
  • button — 0 for a move, which isn't about any one button
  • buttons — every button currently held, as a bitmask
  • detail — how many clicks this is part of; 0 for a move
  • ctrlKey / shiftKey / altKey / metaKey — modifier keys held

mouseenter / mouseleave ​

Same fields as mousedown, above. An element already under the pointer when it mounts gets a mouseenter the first time its hover state is checked, with no pointer movement involved — unlike the DOM, where mouseenter only ever follows an actual move. Its movementX/movementY share the same tracker every other mouse event does, so hovering with no pointer movement since an earlier click can still report a nonzero delta, against that click's position.

Wheel ​

wheel ​

The same fields as mousedown, above, plus WheelEvent's:

  • deltaX / deltaY — scroll distance; positive when scrolling down or right, as in the DOM
  • deltaZ — always 0
  • deltaMode — 0 (pixels) or 1 (lines), never 2 (page)

In a scrolling container, stopPropagation() keeps ancestors' wheel listeners from firing, and the container still scrolls. preventDefault() cancels the scroll. Nested containers scroll the innermost first, and the wheel moves to the next container out once the inner one is at its limit.

The scroll event is not supported.

Keyboard ​

keydown / keyup ​

Fire on whichever node is focused, bubbling to its ancestors — with nothing focused, neither reaches any node. A subset of the DOM's KeyboardEvent's fields:

  • key — DOM-named where recognized, the raw character otherwise
  • repeat — always false for keyup
  • ctrlKey / shiftKey / altKey / metaKey — modifier keys held

There's no code, location, or isComposing yet.

Focus ​

A node isn't focusable until .focus() has been called on it at least once — clicking a node that's never been focused this way doesn't move focus there, unlike a DOM element with a tabindex.

Once a node has been focused this way, it stays focusable from then on: clicking it moves focus there on its own, the same as a focusable DOM element does. .blur() unfocuses the node only if it is the focused one.

A template ref's own element carries .focus()/.blur() directly, the same as a real DOM element:

vue
<script setup>
import { onMounted, ref } from "@vue/runtime-core";

const input = ref(null);
onMounted(() => input.value.focus());
</script>

<template>
  <div ref="input" @focus="onFocused" @blur="onBlurred">...</div>
</template>

focus/blur carry no fields of their own.

Propagation ​

An event bubbles from the node it fired on up through its ancestors, the same as the DOM's. Every listener receives the same three methods to change that:

vue
<script setup>
function onClick(event) {
  event.stopPropagation();
}
</script>

Every listener runs in the bubble phase; there's no way yet to listen during the capture phase.

Event modifiers ​

Vue's template event modifiers — v-on:click.once, .passive, .capture — are Vue's own syntax, not a web standard.

  • .once is fully real: the listener unbinds after its first call.
  • .passive and .capture are accepted but currently degrade to an ordinary bubble-phase listener, for the same reason given in Propagation above — Incarnative.js has no capture-phase dispatch yet, so .capture doesn't run during the capture phase, and .passive has no effect beyond a plain bind.

Errors in handlers ​

An error an @event handler throws goes to the app's app.config.errorHandler and to any onErrorCaptured hooks, as in the browser. With no errorHandler, the host reports it as an application error. A promise that rejects with no handler, such as an async handler that throws, is reported the same way.

Released under the MIT or Apache-2.0 license.