> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Événements

> Tous les événements du SDK du panier Aftersell : quand chacun se déclenche, ce qu'il vous transmet, à quoi l'utiliser, et les erreurs qui causent des boucles infinies.

Les événements vous permettent d'exécuter du code **quand quelque chose se produit** dans le panier. Ils se trouvent sous `window.aftersell.cart.events`.

S'abonner est un appel de configuration, donc c'est sûr en haut de votre script, sans besoin d'attendre `ready()`.

<div id="available-events">
  ## Événements disponibles
</div>

| Événement                                     | Charge utile                                          | Se déclenche quand                                        |
| --------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/fr/aftersell/cart/sdk-cart-object) | Le panier se charge, une fois par page.                   |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/fr/aftersell/cart/sdk-cart-object) | Le contenu du panier change, après le premier chargement. |
| [`item_added`](#item_added)                   | `{ item }`                                            | Une nouvelle ligne apparaît dans le panier.               |
| [`item_removed`](#item_removed)               | `{ item }`                                            | Une ligne disparaît du panier.                            |
| [`cart_opened`](#cart_opened-and-cart_closed) | Aucune                                                | Le tiroir s'ouvre.                                        |
| [`cart_closed`](#cart_opened-and-cart_closed) | Aucune                                                | Le tiroir se ferme.                                       |
| [`checkout`](#checkout)                       | Aucune                                                | Le bouton de paiement est cliqué.                         |

<div id="subscribing">
  ## S'abonner
</div>

`events.on(event, handler)` enregistre un gestionnaire et **renvoie une fonction qui le désabonne** :

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const off = window.aftersell.cart.events.on('cart_updated', (state) => {
  console.log('Cart total is now', state.totalPrice);
});

// later, to stop listening:
off();
```

* `events.once(event, handler)` : se déclenche une fois, puis se désabonne de lui-même.
* `events.off(event, handler)` : supprime un gestionnaire spécifique.

Un gestionnaire qui lève une exception est isolé et journalisé dans la console ; les autres gestionnaires s'exécutent quand même.

***

<div id="the-two-rules">
  ## Les deux règles
</div>

Presque tous les bugs liés aux événements remontent à l'une d'elles.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### Ne modifiez pas le panier depuis `cart_updated` sans garde-fou
</div>

Modifier le panier à l'intérieur d'un gestionnaire `cart_updated` déclenche à nouveau `cart_updated`. Si ce gestionnaire modifie à nouveau le panier, vous avez une boucle infinie. L'acheteur regarde son panier s'agiter pendant que la page bombarde Shopify.

<Warning>
  **N'appelez jamais une action de manière inconditionnelle depuis `cart_updated` ou `cart_loaded`.** Protégez-la avec une vérification de l'état que vous êtes sur le point de créer, afin que le second passage ne fasse rien.
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ❌ Loops forever: every add triggers an update, which triggers another add.
window.aftersell.cart.events.on('cart_updated', (state) => {
  window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
});

// ✅ Guarded: once the gift is present, the condition is false and it stops.
window.aftersell.cart.events.on('cart_updated', (state) => {
  const hasGift = state.items.some((line) => line.variantId === GIFT_VARIANT_ID);
  if (state.totalPrice >= 5000 && !hasGift) {
    window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
  }
});
```

Le panier vous offre bien un filet de sécurité : une mise à jour qui produit un panier **identique** n'émet rien, donc un rechargement qui ne change rien ne relancera pas le cycle. Cela vous protège des boucles sans effet accidentelles. Cela ne vous protège **pas** d'un gestionnaire qui modifie réellement le panier à chaque fois.

<div id="treat-the-payload-as-read-only">
  ### Traitez la charge utile comme en lecture seule
</div>

Chaque gestionnaire d'un même événement reçoit le *même* objet. Le muter change ce que voient les gestionnaires exécutés après le vôtre, y compris les gestionnaires appartenant à d'autres applications de la boutique.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ❌ Corrupts the payload for every later handler.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items = state.items.filter((line) => line.finalLinePrice > 0);
});

// ✅ Copy first.
window.aftersell.cart.events.on('cart_updated', (state) => {
  const paidItems = state.items.filter((line) => line.finalLinePrice > 0);
});
```

Pour modifier réellement le panier, utilisez une [action](/fr/aftersell/cart/sdk-actions). Pour modifier le rendu des lignes, utilisez [`registerLineTransform`](/fr/aftersell/cart/sdk-hooks#registerlinetransform).

***

<div id="cart_loaded">
  ## cart\_loaded
</div>

Se déclenche **une fois**, au premier chargement du panier sur la page. La charge utile est l'[objet cart](/fr/aftersell/cart/sdk-cart-object) complet.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_loaded', (state) => {
  console.log('Page loaded with', state.itemCount, 'items');
});
```

**À utiliser pour :** tout ce qui doit s'exécuter sur l'état initial du panier, comme réconcilier un cadeau gratuit, initialiser un widget ou signaler le contenu du panier à un outil d'analyse au chargement de la page.

**`cart_loaded` est rejoué pour les abonnés tardifs.** Si vous vous abonnez après que le panier a déjà été chargé, votre gestionnaire est appelé immédiatement avec le panier actuel. L'ordre d'abonnement n'a jamais d'importance, vous n'avez donc pas à vous soucier de savoir si votre script a devancé le panier.

<Tip>
  Une logique qui doit être correcte à la fois au chargement de la page et à chaque modification ultérieure doit s'abonner à **la fois** à `cart_loaded` et à `cart_updated` avec la même fonction. C'est le schéma standard pour « garder X synchronisé avec le panier ».
</Tip>

<div id="cart_updated">
  ## cart\_updated
</div>

Se déclenche chaque fois que le contenu du panier change **après** le premier chargement, que ce soit depuis le tiroir, depuis vos propres actions, depuis le thème ou depuis une autre application. La charge utile est l'[objet cart](/fr/aftersell/cart/sdk-cart-object) complet.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  document.querySelector('#my-total').textContent =
    window.aftersell.cart.actions.formatMoney(state.totalPrice);
});
```

**À utiliser pour :** garder synchronisé quelque chose en dehors du panier, comme un total personnalisé, une barre de progression, un badge d'en-tête ou un événement d'analyse à chaque modification.

Une mise à jour qui produit un panier identique n'émet rien. Rouvrir le tiroir, revenir sur l'onglet ou un rechargement qui renvoie le même contenu ne le déclenchera pas.

<Warning>
  Relisez [les deux règles](#the-two-rules) avant d'appeler une action ici.
</Warning>

<div id="item_added">
  ## item\_added
</div>

Se déclenche lorsqu'une **nouvelle ligne** apparaît dans le panier. La charge utile est `{ item }`, où `item` est la [ligne du panier](/fr/aftersell/cart/sdk-cart-object#cart-lines).

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('item_added', (payload) => {
  myAnalytics.track('Added to cart', {
    id: payload.item.variantId,
    title: payload.item.title,
    quantity: payload.item.quantity,
  });
});
```

**À utiliser pour :** le suivi des ajouts au panier dans un outil d'analyse tiers. C'est l'usage le plus courant du SDK. Consultez [suivre les ajouts au panier](/fr/aftersell/cart/sdk-use-case-analytics).

Deux choses à savoir sur la façon dont il est dérivé :

<Warning>
  **Un changement de quantité n'est pas un ajout.** Le panier détermine les ajouts et les suppressions en comparant les *lignes*, pas les quantités. Un acheteur qui fait passer une ligne de 1 à 3 déclenche `cart_updated`, pas `item_added`. Si vous devez aussi capturer les augmentations de quantité, comparez avec l'état précédent dans un gestionnaire `cart_updated`.
</Warning>

Il ne se déclenche pas non plus pour les articles déjà présents dans le panier au chargement de la page ; ceux-là arrivent via `cart_loaded`. Ajouter plusieurs produits distincts à la fois déclenche l'événement une fois par ligne.

<div id="item_removed">
  ## item\_removed
</div>

Se déclenche lorsqu'une ligne disparaît du panier. La charge utile est `{ item }`, la ligne telle qu'elle était juste avant de disparaître, vous pouvez donc encore lire ses `key`, `variantId` et `title`.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('item_removed', (payload) => {
  console.log('Removed', payload.item.title);
});
```

**À utiliser pour :** annuler quelque chose que vous avez fait à l'ajout, comme effacer un indicateur, réafficher une offre que l'acheteur a déclinée ou signaler les suppressions à un outil d'analyse.

Même mise en garde que pour `item_added` : réduire une quantité sans atteindre zéro n'est pas une suppression.

<div id="cart_opened-and-cart_closed">
  ## cart\_opened et cart\_closed
</div>

Se déclenchent lorsque le tiroir s'ouvre et se ferme. Pas de charge utile.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_opened', () => {
  myAnalytics.track('Cart viewed');
});

window.aftersell.cart.events.on('cart_closed', () => {
  document.body.classList.remove('cart-is-open');
});
```

**À utiliser pour :** le suivi des vues, mettre en pause une vidéo ou un carrousel derrière le tiroir, basculer une classe sur la page.

Aucun des deux ne se déclenche au chargement initial de la page, seulement lors d'une ouverture ou fermeture réelle.

<div id="checkout">
  ## checkout
</div>

Se déclenche lorsque l'acheteur clique sur le bouton de paiement, immédiatement avant que le navigateur ne navigue. Pas de charge utile.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('checkout', () => {
  myAnalytics.track('Checkout started');
});
```

**À utiliser pour :** le suivi de l'intention de paiement.

<Warning>
  **Vous ne pouvez pas annuler le paiement depuis ce gestionnaire.** L'événement est une notification, pas une barrière ; la navigation se produit quoi que fasse votre code. Gardez le gestionnaire rapide et synchrone : un `await` ou un appel réseau lent peut ne pas se terminer avant le déchargement de la page. Utilisez [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) pour tout ce que vous devez envoyer de manière fiable.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## Écouter depuis l'extérieur du SDK
</div>

Chaque événement est également émis en tant que `CustomEvent` DOM sur `window`, vous pouvez donc écouter sans toucher à `window.aftersell.cart`. C'est utile depuis un fichier de thème, une application tierce ou un script qui se charge indépendamment du panier.

| Événement du bus | Événement DOM                 |
| ---------------- | ----------------------------- |
| `cart_loaded`    | `aftersell:cart:cart-loaded`  |
| `cart_updated`   | `aftersell:cart:cart-updated` |
| `item_added`     | `aftersell:cart:item-added`   |
| `item_removed`   | `aftersell:cart:item-removed` |
| `cart_opened`    | `aftersell:cart:cart-opened`  |
| `cart_closed`    | `aftersell:cart:cart-closed`  |
| `checkout`       | `aftersell:cart:checkout`     |

Attention à la convention de nommage : le bus utilise le `snake_case`, les événements DOM utilisent le `kebab-case` derrière un préfixe `aftersell:cart:`.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.addEventListener('aftersell:cart:cart-updated', (event) => {
  console.log('Cart total is now', event.detail.totalPrice);
});
```

La charge utile arrive sur `event.detail` et correspond à l'[objet cart](/fr/aftersell/cart/sdk-cart-object). Les événements sont émis sur `window`, donc un écouteur n'importe où sur la page les reçoit. Le panier est rendu dans un shadow root, mais la frontière du shadow n'est jamais sur le chemin de l'événement. Chaque émission clone la charge utile, donc un écouteur qui mute `event.detail` ne peut affecter personne d'autre, et un écouteur qui lève une exception ne peut pas perturber le SDK.

<Warning>
  **`cart-loaded` n'est pas rejoué sur le DOM.** Le bus rejoue `cart_loaded` pour les abonnés tardifs, mais ce chemin contourne l'émission DOM, donc un `window.addEventListener('aftersell:cart:cart-loaded')` enregistré après que le panier a déjà été chargé ne se déclenchera jamais. Si l'ordre de chargement de votre script n'est pas garanti, utilisez `window.aftersell.cart.events.on('cart_loaded', …)`, qui rejoue bien, ou écoutez aussi `aftersell:cart:cart-updated`.
</Warning>

<div id="shopify-standard-cart-events">
  ### Événements de panier standard Shopify
</div>

Séparément, le panier publie les [événements de panier standard](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) de Shopify sur `document` chaque fois qu'il modifie le panier, afin que le code de thème et les autres applications puissent réagir aux mutations d'Aftersell de la même manière qu'ils réagissent à celles du thème :

| Événement                      | Charge utile sur l'instance de l'événement                                       |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `shopify:cart:lines-update`    | `action: 'add' \| 'update' \| 'remove'`, `context: 'cart' \| 'product'`, `lines` |
| `shopify:cart:note-update`     | `context: 'cart'`, `note`                                                        |
| `shopify:cart:discount-update` | `discountCodes: [{ code }]`                                                      |

<Warning>
  **La charge utile n'est pas sur `event.detail`.** `detail` ne porte que `{ source: 'aftersell' }`, le marqueur que le panier utilise pour ignorer ses propres événements au lieu de boucler. Tout ce qui figure dans le tableau ci-dessus est assigné directement sur l'objet événement, donc lisez `event.action`, pas `event.detail.action`.
</Warning>

Chaque événement porte aussi une `promise` qu'Aftersell règle lorsque l'écriture sous-jacente aboutit, conformément au standard de Shopify : attendez-la avec `await`, ne la résolvez pas vous-même. Ces événements sont émis sur `document` et remontent par bubbling, donc un écouteur sur `window` les reçoit aussi.

<div id="where-to-go-next">
  ## Pour aller plus loin
</div>

* **[Objet cart](/fr/aftersell/cart/sdk-cart-object)** : la forme complète des charges utiles ci-dessus.
* **[Actions](/fr/aftersell/cart/sdk-actions)** : comment modifier le panier depuis un gestionnaire.
* **[Hooks](/fr/aftersell/cart/sdk-hooks)** : pour modifier le rendu du panier, plutôt que d'y réagir.
* **[Cas d'usage](/fr/aftersell/cart/sdk-use-cases)** : suivi analytique, cadeaux gratuits et autres exemples complets.
