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

> Jedes Aftersell Cart SDK-Event: wann jedes ausgelöst wird, was es dir übergibt, wofür du es verwendest und die Fehler, die Endlosschleifen verursachen.

Mit Events kannst du Code ausführen, **wenn etwas** im Warenkorb **passiert**. Sie liegen unter `window.aftersell.cart.events`.

Das Abonnieren ist ein Setup-Aufruf und daher am Anfang deines Scripts sicher, ohne auf `ready()` warten zu müssen.

<div id="available-events">
  ## Verfügbare Events
</div>

| Event                                         | Payload                                               | Wird ausgelöst, wenn                                     |
| --------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/de/aftersell/cart/sdk-cart-object) | Der Warenkorb lädt, einmal pro Seite.                    |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/de/aftersell/cart/sdk-cart-object) | Der Warenkorb-Inhalt sich ändert, nach dem ersten Laden. |
| [`item_added`](#item_added)                   | `{ item }`                                            | Eine neue Zeile im Warenkorb erscheint.                  |
| [`item_removed`](#item_removed)               | `{ item }`                                            | Eine Zeile aus dem Warenkorb verschwindet.               |
| [`cart_opened`](#cart_opened-and-cart_closed) | Keine                                                 | Der Drawer sich öffnet.                                  |
| [`cart_closed`](#cart_opened-and-cart_closed) | Keine                                                 | Der Drawer sich schließt.                                |
| [`checkout`](#checkout)                       | Keine                                                 | Der Checkout-Button geklickt wird.                       |

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

`events.on(event, handler)` registriert einen Handler und **gibt eine Funktion zurück, die ihn abmeldet**:

```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)`: wird einmal ausgelöst und meldet sich dann selbst ab.
* `events.off(event, handler)`: entfernt einen bestimmten Handler.

Ein Handler, der wirft, wird isoliert und in der Konsole protokolliert; die anderen Handler laufen weiter.

***

<div id="the-two-rules">
  ## Die zwei Regeln
</div>

Fast jeder Event-Bug lässt sich auf eine davon zurückführen.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### Ändere den Warenkorb nicht ungeschützt aus `cart_updated`
</div>

Den Warenkorb innerhalb eines `cart_updated`-Handlers zu ändern löst erneut `cart_updated` aus. Wenn dieser Handler den Warenkorb wieder ändert, hast du eine Endlosschleife. Der Käufer sieht seinen Warenkorb flackern, während die Seite Shopify bombardiert.

<Warning>
  **Rufe nie bedingungslos eine Action aus `cart_updated` oder `cart_loaded` auf.** Schütze sie mit einer Prüfung auf den Zustand, den du gleich erzeugst, sodass der zweite Durchlauf nichts tut.
</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);
  }
});
```

Der Warenkorb gibt dir ein Sicherheitsnetz: Ein Update, das einen **identischen** Warenkorb erzeugt, sendet nichts, sodass ein Refetch, der nichts ändert, den Zyklus nicht neu startet. Das schützt dich vor versehentlichen No-op-Schleifen. Es schützt dich **nicht** vor einem Handler, der den Warenkorb tatsächlich jedes Mal ändert.

<div id="treat-the-payload-as-read-only">
  ### Behandle die Payload als schreibgeschützt
</div>

Jeder Handler eines Events erhält *dasselbe* Objekt. Es zu mutieren ändert, was die Handler nach deinem sehen, einschließlich der Handler, die zu anderen Apps im Shop gehören.

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

Um den Warenkorb tatsächlich zu ändern, verwende eine [Action](/de/aftersell/cart/sdk-actions). Um zu ändern, wie Zeilen gerendert werden, verwende [`registerLineTransform`](/de/aftersell/cart/sdk-hooks#registerlinetransform).

***

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

Wird **einmal** ausgelöst, wenn der Warenkorb erstmals auf der Seite lädt. Die Payload ist das vollständige [Cart-Objekt](/de/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');
});
```

**Verwende es für:** alles, was gegen den Ausgangszustand des Warenkorbs laufen muss, etwa ein Gratisgeschenk abgleichen, ein Widget initialisieren oder den Warenkorb-Inhalt beim Seitenaufruf an Analytics melden.

**`cart_loaded` wird für späte Abonnenten erneut abgespielt.** Wenn du abonnierst, nachdem der Warenkorb bereits geladen ist, wird dein Handler sofort mit dem aktuellen Warenkorb aufgerufen. Die Abonnement-Reihenfolge spielt nie eine Rolle, du musst dir also keine Sorgen machen, ob dein Script schneller war als der Warenkorb.

<Tip>
  Logik, die sowohl beim Seitenaufruf als auch bei jeder Änderung danach korrekt sein muss, sollte **beide** Events `cart_loaded` und `cart_updated` mit derselben Funktion abonnieren. Das ist das Standardmuster für „X mit dem Warenkorb synchron halten“.
</Tip>

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

Wird jedes Mal ausgelöst, wenn sich der Warenkorb-Inhalt **nach** dem ersten Laden ändert — sei es aus dem Drawer, durch deine eigenen Actions, durch das Theme oder durch eine andere App. Die Payload ist das vollständige [Cart-Objekt](/de/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);
});
```

**Verwende es für:** etwas außerhalb des Warenkorbs synchron halten, etwa eine eigene Gesamtsumme, einen Fortschrittsbalken, ein Header-Badge oder ein Analytics-Event bei jeder Änderung.

Ein Update, das einen identischen Warenkorb erzeugt, sendet nichts. Den Drawer erneut öffnen, zum Tab zurückwechseln oder ein Refetch, der denselben Inhalt zurückgibt, löst es nicht aus.

<Warning>
  Lies [die zwei Regeln](#the-two-rules) erneut, bevor du hier eine Action aufrufst.
</Warning>

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

Wird ausgelöst, wenn eine **neue Zeile** im Warenkorb erscheint. Die Payload ist `{ item }`, wobei `item` die [Warenkorb-Zeile](/de/aftersell/cart/sdk-cart-object#cart-lines) ist.

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

**Verwende es für:** Add-to-cart-Tracking in einem Drittanbieter-Analytics-Tool. Das ist der mit Abstand häufigste Einsatz des SDK. Siehe [Add-to-cart tracken](/de/aftersell/cart/sdk-use-case-analytics).

Zwei Dinge, die du über seine Herleitung wissen solltest:

<Warning>
  **Eine Mengenänderung ist kein Hinzufügen.** Der Warenkorb ermittelt Hinzufügungen und Entfernungen durch Diffen der *Zeilen*, nicht der Mengen. Ein Käufer, der eine Zeile von 1 auf 3 erhöht, löst `cart_updated` aus, nicht `item_added`. Wenn du auch Mengenerhöhungen erfassen musst, vergleiche in einem `cart_updated`-Handler mit dem vorherigen Zustand.
</Warning>

Es wird auch nicht für Artikel ausgelöst, die beim Seitenaufruf bereits im Warenkorb waren; die kommen über `cart_loaded` an. Das gleichzeitige Hinzufügen mehrerer unterschiedlicher Produkte löst das Event einmal pro Zeile aus.

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

Wird ausgelöst, wenn eine Zeile aus dem Warenkorb verschwindet. Die Payload ist `{ item }`, die Zeile wie sie kurz vor ihrem Verschwinden war — du kannst also weiterhin `key`, `variantId` und `title` lesen.

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

**Verwende es für:** etwas rückgängig machen, das du beim Hinzufügen getan hast, etwa ein Flag löschen, ein vom Käufer abgelehntes Angebot wieder einblenden oder Entfernungen an Analytics melden.

Derselbe Vorbehalt wie bei `item_added`: Eine Menge zu senken, ohne null zu erreichen, ist keine Entfernung.

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

Werden ausgelöst, wenn sich der Drawer öffnet und schließt. Keine 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');
});
```

**Verwende sie für:** View-Tracking, ein Video oder Karussell hinter dem Drawer pausieren, eine Klasse auf der Seite umschalten.

Keines wird beim initialen Seitenaufruf ausgelöst, nur bei einem tatsächlichen Öffnen oder Schließen.

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

Wird ausgelöst, wenn der Käufer auf den Checkout-Button klickt, unmittelbar bevor der Browser navigiert. Keine Payload.

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

**Verwende es für:** Checkout-Intent-Tracking.

<Warning>
  **Du kannst den Checkout aus diesem Handler nicht abbrechen.** Das Event ist eine Benachrichtigung, kein Gate; die Navigation passiert unabhängig davon, was dein Code tut. Halte den Handler schnell und synchron: Ein `await` oder ein langsamer Netzwerkaufruf wird möglicherweise nicht fertig, bevor die Seite entladen wird. Verwende [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) für alles, was du zuverlässig senden musst.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## Von außerhalb des SDK lauschen
</div>

Jedes Event wird auch als DOM-`CustomEvent` auf `window` gesendet, sodass du lauschen kannst, ohne `window.aftersell.cart` anzufassen. Das ist nützlich aus einer Theme-Datei, einer Drittanbieter-App oder einem Script, das unabhängig vom Warenkorb lädt.

| 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`     |

Achte auf die Benennung: Der Bus verwendet `snake_case`, die DOM-Events verwenden `kebab-case` hinter einem `aftersell:cart:`-Präfix.

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

Die Payload kommt auf `event.detail` an und entspricht dem [Cart-Objekt](/de/aftersell/cart/sdk-cart-object). Events werden auf `window` gesendet, sodass ein Listener überall auf der Seite sie empfängt. Der Warenkorb wird in einem Shadow Root gerendert, aber die Shadow-Grenze liegt nie im Pfad des Events. Jeder Versand klont die Payload, sodass ein Listener, der `event.detail` mutiert, niemand anderen beeinflussen kann, und ein Listener, der wirft, das SDK nicht stören kann.

<Warning>
  **`cart-loaded` wird im DOM nicht erneut abgespielt.** Der Bus spielt `cart_loaded` für späte Abonnenten erneut ab, aber dieser Pfad umgeht den DOM-Versand — ein `window.addEventListener('aftersell:cart:cart-loaded')`, das registriert wird, nachdem der Warenkorb bereits geladen ist, wird also nie ausgelöst. Wenn die Ladereihenfolge deines Scripts nicht garantiert ist, verwende `window.aftersell.cart.events.on('cart_loaded', …)`, das erneut abspielt, oder lausche zusätzlich auf `aftersell:cart:cart-updated`.
</Warning>

<div id="shopify-standard-cart-events">
  ### Shopify-Standard-Warenkorb-Events
</div>

Unabhängig davon veröffentlicht der Warenkorb Shopifys [Standard-Warenkorb-Events](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) auf `document`, wann immer er den Warenkorb ändert — Theme-Code und andere Apps können also auf Aftersells Mutationen genauso reagieren wie auf die des Themes:

| Event                          | Payload auf der Event-Instanz                                                    |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `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>
  **Die Payload liegt nicht auf `event.detail`.** `detail` trägt nur `{ source: 'aftersell' }` — das Tag, mit dem der Warenkorb seine eigenen Events ignoriert, statt in eine Schleife zu geraten. Alles in der Tabelle oben wird direkt dem Event-Objekt zugewiesen — lies also `event.action`, nicht `event.detail.action`.
</Warning>

Jedes Event trägt außerdem ein `promise`, das Aftersell auflöst, wenn der zugrunde liegende Schreibvorgang abgeschlossen ist, entsprechend Shopifys Standard — warte darauf, löse es nicht selbst auf. Diese werden auf `document` gesendet und bubbeln, sodass ein `window`-Listener sie ebenfalls empfängt.

<div id="where-to-go-next">
  ## Nächste Schritte
</div>

* **[Cart-Objekt](/de/aftersell/cart/sdk-cart-object)**: die vollständige Struktur der obigen Payloads.
* **[Actions](/de/aftersell/cart/sdk-actions)**: wie du den Warenkorb aus einem Handler änderst.
* **[Hooks](/de/aftersell/cart/sdk-hooks)**: um zu ändern, wie der Warenkorb rendert, statt auf ihn zu reagieren.
* **[Use Cases](/de/aftersell/cart/sdk-use-cases)**: Analytics-Tracking, Gratisgeschenke und andere vollständige Beispiele.
