> ## 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.

# Events

> Elk Aftersell Cart SDK-event: wanneer elk afgaat, wat het je geeft, waarvoor je het gebruikt, en de fouten die oneindige loops veroorzaken.

Met events kun je code draaien **wanneer er iets gebeurt** in de winkelwagen. Ze staan onder `window.aftersell.cart.events`.

Abonneren is een set-up-aanroep, dus veilig bovenaan je script, zonder dat je op `ready()` hoeft te wachten.

<div id="available-events">
  ## Beschikbare events
</div>

| Event                                         | Payload                                               | Gaat af wanneer                                            |
| --------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/nl/aftersell/cart/sdk-cart-object) | De winkelwagen laadt, eenmaal per pagina.                  |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/nl/aftersell/cart/sdk-cart-object) | De inhoud van de winkelwagen verandert, na de eerste load. |
| [`item_added`](#item_added)                   | `{ item }`                                            | Een nieuwe regel verschijnt in de winkelwagen.             |
| [`item_removed`](#item_removed)               | `{ item }`                                            | Een regel verdwijnt uit de winkelwagen.                    |
| [`cart_opened`](#cart_opened-and-cart_closed) | Geen                                                  | De drawer opent.                                           |
| [`cart_closed`](#cart_opened-and-cart_closed) | Geen                                                  | De drawer sluit.                                           |
| [`checkout`](#checkout)                       | Geen                                                  | Er wordt op de checkout-knop geklikt.                      |

<div id="subscribing">
  ## Abonneren
</div>

`events.on(event, handler)` registreert een handler en **geeft een functie terug die het abonnement opzegt**:

```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)`: gaat één keer af en schrijft zichzelf daarna uit.
* `events.off(event, handler)`: verwijdert een specifieke handler.

Een handler die een fout gooit wordt geïsoleerd en naar de console gelogd; de andere handlers draaien gewoon door.

***

<div id="the-two-rules">
  ## De twee regels
</div>

Bijna elke event-bug is terug te voeren op een van deze.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### Wijzig de winkelwagen niet vanuit `cart_updated` zonder guard
</div>

De winkelwagen wijzigen binnen een `cart_updated`-handler laat `cart_updated` opnieuw afgaan. Als die handler de winkelwagen opnieuw wijzigt, heb je een oneindige loop. De shopper ziet zijn winkelwagen wild heen en weer gaan terwijl de pagina Shopify bestookt.

<Warning>
  **Roep nooit onvoorwaardelijk een action aan vanuit `cart_updated` of `cart_loaded`.** Beveilig hem met een check op de staat die je gaat creëren, zodat de tweede doorloop niets doet.
</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);
  }
});
```

De winkelwagen geeft je wel één vangnet: een update die een **identieke** winkelwagen oplevert stuurt niets uit, dus een refetch die niets verandert start de cyclus niet opnieuw. Dat beschermt je tegen onbedoelde no-op-loops. Het beschermt je **niet** tegen een handler die de winkelwagen elke keer echt verandert.

<div id="treat-the-payload-as-read-only">
  ### Behandel de payload als alleen-lezen
</div>

Elke handler voor één event ontvangt *hetzelfde* object. Muteren ervan verandert wat de handlers na de jouwe zien, inclusief handlers van andere apps in de winkel.

```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);
});
```

Om de winkelwagen echt te wijzigen, gebruik je een [action](/nl/aftersell/cart/sdk-actions). Om te wijzigen hoe regels renderen, gebruik je [`registerLineTransform`](/nl/aftersell/cart/sdk-hooks#registerlinetransform).

***

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

Gaat **één keer** af, wanneer de winkelwagen voor het eerst op de pagina laadt. De payload is het volledige [cart-object](/nl/aftersell/cart/sdk-cart-object).

```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');
});
```

**Gebruik het voor:** alles wat tegen de startstaat van de winkelwagen moet draaien, zoals een gratis geschenk afstemmen, een widget initialiseren of de winkelwageninhoud bij page load rapporteren aan analytics.

**`cart_loaded` wordt herafgespeeld voor late abonnees.** Als je je abonneert nadat de winkelwagen al is geladen, wordt je handler direct aangeroepen met de huidige winkelwagen. De volgorde van abonneren maakt nooit uit, dus je hoeft je geen zorgen te maken of je script sneller was dan de winkelwagen.

<Tip>
  Logica die zowel bij page load als bij elke wijziging daarna correct moet zijn, moet zich met dezelfde functie op **zowel** `cart_loaded` als `cart_updated` abonneren. Dat is het standaardpatroon voor "houd X in sync met de winkelwagen".
</Tip>

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

Gaat elke keer af dat de inhoud van de winkelwagen verandert **na** de eerste load, of dat nu via de drawer is, via je eigen actions, via het thema of via een andere app. De payload is het volledige [cart-object](/nl/aftersell/cart/sdk-cart-object).

```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);
});
```

**Gebruik het voor:** iets buiten de winkelwagen in sync houden, zoals een eigen totaal, een voortgangsbalk, een headerbadge of een analytics-event bij elke wijziging.

Een update die een identieke winkelwagen oplevert stuurt niets uit. De drawer opnieuw openen, terugschakelen naar het tabblad of een refetch die dezelfde inhoud teruggeeft, laat het niet afgaan.

<Warning>
  Herlees [de twee regels](#the-two-rules) voordat je hier een action aanroept.
</Warning>

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

Gaat af wanneer er een **nieuwe regel** in de winkelwagen verschijnt. De payload is `{ item }`, waarbij `item` de [winkelwagenregel](/nl/aftersell/cart/sdk-cart-object#cart-lines) is.

```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,
  });
});
```

**Gebruik het voor:** add-to-cart-tracking in een externe analytics-tool. Dit is verreweg de meest voorkomende toepassing van de SDK. Zie [add-to-cart tracken](/nl/aftersell/cart/sdk-use-case-analytics).

Twee dingen om te weten over hoe het wordt afgeleid:

<Warning>
  **Een aantalswijziging is geen toevoeging.** De winkelwagen bepaalt toevoegingen en verwijderingen door *regels* te diffen, niet aantallen. Een shopper die een regel van 1 naar 3 verhoogt, triggert `cart_updated`, niet `item_added`. Als je ook aantalverhogingen wilt opvangen, vergelijk dan met de vorige staat in een `cart_updated`-handler.
</Warning>

Het gaat ook niet af voor artikelen die al in de winkelwagen zaten toen de pagina laadde; die komen binnen via `cart_loaded`. Meerdere verschillende producten tegelijk toevoegen laat het event één keer per regel afgaan.

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

Gaat af wanneer een regel uit de winkelwagen verdwijnt. De payload is `{ item }`, de regel zoals hij was net voordat hij verdween, dus je kunt nog steeds zijn `key`, `variantId` en `title` lezen.

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

**Gebruik het voor:** iets terugdraaien wat je bij het toevoegen deed, zoals een vlag wissen, een aanbieding die de shopper afsloeg opnieuw tonen, of verwijderingen rapporteren aan analytics.

Dezelfde kanttekening als bij `item_added`: een aantal verlagen zonder nul te raken is geen verwijdering.

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

Gaan af wanneer de drawer opent en sluit. Geen payload.

```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');
});
```

**Gebruik het voor:** view-tracking, een video of carrousel achter de drawer pauzeren, een class op de pagina togglen.

Geen van beide gaat af bij de initiële page load, alleen bij een daadwerkelijk openen of sluiten.

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

Gaat af wanneer de shopper op de checkout-knop klikt, onmiddellijk voordat de browser navigeert. Geen payload.

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

**Gebruik het voor:** tracking van checkout-intentie.

<Warning>
  **Je kunt de checkout niet annuleren vanuit deze handler.** Het event is een notificatie, geen poort; navigatie vindt plaats ongeacht wat je code doet. Houd de handler snel en synchroon: een `await` of een trage netwerkcall is mogelijk niet klaar voordat de pagina unloadt. Gebruik [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) voor alles wat je betrouwbaar moet versturen.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## Luisteren van buiten de SDK
</div>

Elk event wordt ook gedispatcht als een DOM-`CustomEvent` op `window`, dus je kunt luisteren zonder `window.aftersell.cart` aan te raken. Dat is handig vanuit een themabestand, een externe app of een script dat onafhankelijk van de winkelwagen laadt.

| Bus-event      | DOM-event                     |
| -------------- | ----------------------------- |
| `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`     |

Let op de naamgeving: de bus gebruikt `snake_case`, de DOM-events gebruiken `kebab-case` achter een `aftersell:cart:`-prefix.

```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);
});
```

De payload komt binnen op `event.detail` en komt overeen met het [cart-object](/nl/aftersell/cart/sdk-cart-object). Events worden gedispatcht op `window`, dus een listener waar dan ook op de pagina ontvangt ze. De winkelwagen rendert in een shadow root, maar de shadow-grens zit nooit in het pad van het event. Elke dispatch kloont de payload, dus een listener die `event.detail` muteert kan niemand anders beïnvloeden, en een listener die een fout gooit kan de SDK niet verstoren.

<Warning>
  **`cart-loaded` wordt niet herafgespeeld op de DOM.** De bus speelt `cart_loaded` opnieuw af voor late abonnees, maar dat pad omzeilt de DOM-dispatch, dus een `window.addEventListener('aftersell:cart:cart-loaded')` geregistreerd nadat de winkelwagen al is geladen zal nooit afgaan. Als de laadvolgorde van je script niet gegarandeerd is, gebruik dan `window.aftersell.cart.events.on('cart_loaded', …)`, die wel herafspeelt, of luister ook naar `aftersell:cart:cart-updated`.
</Warning>

<div id="shopify-standard-cart-events">
  ### Shopify standaard cart-events
</div>

Daarnaast publiceert de winkelwagen Shopify's [standaard cart-events](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) op `document` telkens wanneer hij de winkelwagen wijzigt, zodat themacode en andere apps op Aftersell's mutaties kunnen reageren op dezelfde manier als op die van het thema:

| Event                          | Payload op de event-instantie                                                    |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `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>
  **De payload zit niet op `event.detail`.** `detail` bevat alleen `{ source: 'aftersell' }` — de tag die de winkelwagen gebruikt om zijn eigen events te negeren in plaats van te loopen. Alles in de tabel hierboven wordt direct op het event-object gezet, dus lees `event.action`, niet `event.detail.action`.
</Warning>

Elk event draagt ook een `promise` die Aftersell settelt wanneer de onderliggende write landt, conform Shopify's standaard — await hem, resolve hem niet. Deze worden gedispatcht op `document` en bubbelen, dus een `window`-listener ontvangt ze ook.

<div id="where-to-go-next">
  ## Waar nu naartoe
</div>

* **[Cart-object](/nl/aftersell/cart/sdk-cart-object)**: de volledige vorm van de payloads hierboven.
* **[Actions](/nl/aftersell/cart/sdk-actions)**: hoe je de winkelwagen wijzigt vanuit een handler.
* **[Hooks](/nl/aftersell/cart/sdk-hooks)**: om te wijzigen hoe de winkelwagen rendert, in plaats van erop te reageren.
* **[Use cases](/nl/aftersell/cart/sdk-use-cases)**: analytics-tracking, gratis geschenken en andere complete voorbeelden.
