Vue 3 : gérer les événements avec v-on (@click, $event, modificateurs .prevent, .stop, .once)
vérifié le 7 septembre 2026 · 6 min
v-on:click, ou son raccourci @click, exécute du code quand l’événement se produit. Passez le nom d’une fonction pour recevoir l’événement natif, ou appelez-la avec ses arguments et $event si vous avez besoin des deux. Les modificateurs .prevent, .stop, .once, .enter ou .esc remplacent le code répétitif de gestion d’événement.
Pour l’instant, l’application affiche. Elle ne réagit à rien. La directive v-on attache un écouteur d’événement à un élément et exécute du code quand il se produit. Ce chapitre couvre tout ce qu’on fait avec elle au quotidien : un clic, un survol, une touche, avec ou sans argument, et les modificateurs qui évitent d’écrire preventDefault() à la main.
Le panier
Ajoutez un compteur et un bouton :
<script setup>
import { ref } from 'vue'
const product = ref('T-Shirt')
const image = ref('/images/t-shirt-bleu.svg')
const inventory = ref(100)
const cart = ref(0)
const details = ref(['60 % coton', '30 % laine', '10 % polyester'])
const variants = ref([
{ id: 2234, color: 'Bleu', image: '/images/t-shirt-bleu.svg' },
{ id: 2235, color: 'Rouge', image: '/images/t-shirt-rouge.svg' }
])
</script>
<template>
<div class="product-display">
<div class="cart">Panier ({{ cart }})</div>
…
<button class="button">Ajouter au panier</button>
</div>
</template>Écouter un clic
v-on: suivi du nom de l’événement, avec le code à exécuter entre guillemets :
<button class="button" v-on:click="cart += 1">Ajouter au panier</button>Cliquez : le compteur monte. Le nom de l’événement est celui du DOM standard, sans préfixe : click, submit, input, keyup, mouseover.
Appeler une fonction
Une expression en ligne convient pour une opération triviale. Dès que la logique dépasse une ligne, écrivez une fonction :
<script setup>
function addToCart() {
cart.value += 1
}
</script>
<template>
<button class="button" v-on:click="addToCart">Ajouter au panier</button>
</template>Notez le .value dans le script : nous manipulons la référence, pas la valeur déballée. Dans le gabarit, on écrit toujours {{ cart }} sans .value.
@click="addToCart" passe la fonction elle-même : Vue l’appelle avec l’événement natif en premier argument. @click="addToCart()" l’appelle sans argument. Les deux marchent ici. L’erreur classique arrive quand la fonction attend une valeur : @click="updateImage(variant.image)" est correct, mais oublier les parenthèses transmettrait l’événement à la place du chemin de l’image.
Le raccourci arobase
Comme v-bind a :, v-on a @ :
<button class="button" @click="addToCart">Ajouter au panier</button>C’est l’écriture que vous verrez dans toute la documentation et dans la suite de ce tutoriel.
Passer un argument
Faisons changer l’image au survol d’une couleur. La fonction reçoit le chemin de l’image de la déclinaison survolée :
<script setup>
function updateImage(variantImage) {
image.value = variantImage
}
</script>
<template>
<div
v-for="variant in variants"
:key="variant.id"
@mouseover="updateImage(variant.image)"
>
{{ variant.color }}
</div>
</template>Survolez « Rouge » : l’image devient rouge. Deux mécanismes se combinent ici, la boucle du chapitre 4 et la liaison d’attribut du chapitre 2.
Récupérer l’événement natif
Sans argument, la fonction reçoit l’événement DOM. Avec des arguments, réclamez-le explicitement par $event :
<button @click="voir">Sans argument</button>
<button @click="voirAvec('bleu', $event)">Avec arguments</button>function voir(event) {
console.log(event.target)
}
function voirAvec(couleur, event) {
console.log(couleur, event.target)
}L’objet reçu est l’événement natif du navigateur, sans enveloppe : event.target, event.key, event.preventDefault() fonctionnent comme en JavaScript pur.
Les modificateurs d’événement
Vue ajoute des suffixes qui évitent le code répétitif de gestion d’événement :
<form @submit.prevent="envoyer">…</form> <!-- event.preventDefault() -->
<div @click.stop="…">…</div> <!-- event.stopPropagation() -->
<div @click.self="…">…</div> <!-- seulement si la cible est cet élément -->
<button @click.once="…">…</button> <!-- une seule fois -->
<input @keyup.enter="valider"> <!-- seulement la touche Entrée -->La liste complète, avec ce que chaque modificateur remplace :
| Modificateur | Équivalent JavaScript | Usage typique |
|---|---|---|
.prevent | event.preventDefault() | un formulaire qui ne doit pas recharger la page |
.stop | event.stopPropagation() | un bouton dans une carte cliquable : le clic ne remonte pas à la carte |
.self | if (event.target !== event.currentTarget) return | fermer une fenêtre modale au clic sur le fond, pas sur son contenu |
.once | addEventListener(…, { once: true }) | un bouton « Commander » qui ne doit pas envoyer deux fois |
.capture | addEventListener(…, { capture: true }) | intercepter l’événement avant les enfants |
.passive | addEventListener(…, { passive: true }) | défilement et toucher sur mobile, pour ne pas bloquer le rendu |
Les modificateurs s’enchaînent et se lisent de gauche à droite : @click.stop.prevent arrête la propagation puis annule le comportement par défaut. Cet ordre a un sens avec .self : @click.self.prevent n’annule le défaut que pour les clics sur l’élément lui-même, @click.prevent.self l’annule pour tous les clics, enfants compris.
@submit.prevent servira au chapitre 10 pour empêcher le rechargement de page à l’envoi du formulaire.
Modificateurs de touche et de souris
Sur les événements clavier, un modificateur filtre la touche. Vue accepte tout nom de touche exposé par KeyboardEvent.key, écrit en kebab-case, plus quelques alias :
<input @keyup.enter="valider">
<input @keyup.esc="annuler">
<input @keydown.tab="suivant">
<input @keyup.page-down="pageSuivante"> <!-- KeyboardEvent.key === 'PageDown' -->
<input @keydown.ctrl.enter="envoyer"> <!-- Ctrl + Entrée -->Les alias disponibles : .enter, .tab, .delete (Suppr et Retour arrière), .esc, .space, .up, .down, .left, .right. Les touches système .ctrl, .alt, .shift et .meta se combinent avec les autres. Et .exact exige qu’aucune autre touche système ne soit enfoncée :
<button @click.ctrl="ouvrir">…</button> <!-- Ctrl + clic, même si Shift est aussi enfoncé -->
<button @click.ctrl.exact="ouvrir">…</button> <!-- Ctrl + clic, et rien d'autre -->
<button @click.exact="ouvrir">…</button> <!-- clic sans aucune touche système -->Pour la souris, .left, .right et .middle filtrent le bouton : @click.right.prevent="menuContextuel" remplace le menu du navigateur par le vôtre.
Plusieurs gestionnaires sur un même événement
Un événement peut déclencher plusieurs fonctions, séparées par des virgules. Chaque fonction est alors appelée avec les parenthèses :
<button @click="addToCart(), trackClick('ajout-panier')">Ajouter au panier</button>Au-delà de deux appels, ce n’est plus lisible : écrivez une fonction qui appelle les autres.
Récapitulatif
v-on:clickou@click: une expression en ligne pour une opération triviale, sinon une fonction.- Sans parenthèses, la fonction reçoit l’événement natif, avec des arguments,
$eventle transmet en plus. .prevent,.stop,.self,.onceremplacent le code répétitif, les modificateurs de touche filtrent le clavier,.exactverrouille les combinaisons.- Dans le script, l’état se modifie par
.value, dans le gabarit, sans.
Les fonctions vivent dans l’option methods et accèdent à l’état par this : methods: { addToCart() { this.cart += 1 } }. Le gabarit, lui, ne change pas.
Les événements de ce chapitre viennent du DOM. Pour qu’un composant enfant prévienne son parent, Vue propose des événements personnalisés avec emit : c’est le sujet du chapitre 9, détaillé dans le guide complet de Vue emit.
Erreurs fréquentes
cart += 1 ne fait rien d’utile sur une référence : écrivez cart.value += 1.@click="updateImage" transmet l’événement natif à la place de la valeur attendue. Avec un argument, appelez la fonction : @click="updateImage(variant.image)".$event : @click="faire('bleu', $event)".@click.prevent.self empêche le comportement par défaut de tous les clics, @click.self.prevent seulement de ceux dont la cible est l’élément lui-même. L’ordre se lit de gauche à droite..passive promet au navigateur de ne pas appeler preventDefault(), combiné à .prevent, le navigateur ignore l’un des deux et affiche un avertissement.