Chapitre 5 sur 11

Vue 3 : gérer les événements avec v-on (@click, $event, modificateurs .prevent, .stop, .once)

vérifié le 7 septembre 2026 · 6 min

Réponse rapide

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.

À la fin de ce chapitre, vous saurez réagir aux clics, aux survols et aux touches du clavier, passer des arguments à vos gestionnaires, récupérer l’événement natif et utiliser les modificateurs pour écrire moins de code.

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 :

src/App.vue
<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 :

src/App.vue
<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 :

src/App.vue
<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.

Avec ou sans parenthèses ?

@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 @ :

src/App.vue
<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 :

src/App.vue
<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 :

html
<button @click="voir">Sans argument</button>
<button @click="voirAvec('bleu', $event)">Avec arguments</button>
javascript
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 :

html
<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 :

html
<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 :

html
<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 :

html
<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:click ou @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, $event le transmet en plus.
  • .prevent, .stop, .self, .once remplacent le code répétitif, les modificateurs de touche filtrent le clavier, .exact verrouille les combinaisons.
  • Dans le script, l’état se modifie par .value, dans le gabarit, sans.
En Options API

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.

ExerciceAjoutez un bouton « Retirer du panier » qui décrémente le compteur sans jamais passer sous zéro. Puis remplacez le survol par un clic sur les couleurs, et comparez le ressenti.

Erreurs fréquentes

Oublier .value dans une fonction Dans le script, cart += 1 ne fait rien d’utile sur une référence : écrivez cart.value += 1.
Parenthèses manquantes avec un argument @click="updateImage" transmet l’événement natif à la place de la valeur attendue. Avec un argument, appelez la fonction : @click="updateImage(variant.image)".
Récupérer l’événement quand on passe déjà un argument Il faut le demander explicitement avec $event : @click="faire('bleu', $event)".
Enchaîner les modificateurs dans le mauvais ordre @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.
Utiliser .passive avec .prevent .passive promet au navigateur de ne pas appeler preventDefault(), combiné à .prevent, le navigateur ignore l’un des deux et affiche un avertissement.
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.