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

> Alle Aftersell Cart SDK-events: hvornår hver enkelt affyres, hvad den giver dig, hvad du kan bruge den til, og de fejl der skaber uendelige løkker.

Events lader dig køre kode, **når noget sker** i kurven. De ligger under `window.aftersell.cart.events`.

At abonnere er et opsætningskald, så det er sikkert øverst i dit script uden behov for at vente på `ready()`.

<div id="available-events">
  ## Tilgængelige events
</div>

| Event                                         | Payload                                               | Affyres når                                          |
| --------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/da/aftersell/cart/sdk-cart-object) | Kurven indlæses, én gang pr. side.                   |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/da/aftersell/cart/sdk-cart-object) | Kurvens indhold ændres, efter den første indlæsning. |
| [`item_added`](#item_added)                   | `{ item }`                                            | En ny linje dukker op i kurven.                      |
| [`item_removed`](#item_removed)               | `{ item }`                                            | En linje forsvinder fra kurven.                      |
| [`cart_opened`](#cart_opened-and-cart_closed) | Ingen                                                 | Draweren åbner.                                      |
| [`cart_closed`](#cart_opened-and-cart_closed) | Ingen                                                 | Draweren lukker.                                     |
| [`checkout`](#checkout)                       | Ingen                                                 | Der klikkes på checkout-knappen.                     |

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

`events.on(event, handler)` registrerer en handler og **returnerer en funktion, der afmelder den**:

```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)`: affyres én gang og afmelder derefter sig selv.
* `events.off(event, handler)`: fjerner en specifik handler.

En handler, der kaster en fejl, isoleres og logges til konsollen; de andre handlers kører stadig.

***

<div id="the-two-rules">
  ## De to regler
</div>

Næsten alle event-fejl kan spores tilbage til en af disse.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### Ændr ikke kurven fra `cart_updated` uden en guard
</div>

At ændre kurven inde i en `cart_updated`-handler affyrer `cart_updated` igen. Hvis den handler ændrer kurven igen, har du en uendelig løkke. Kunden ser sin kurv gå amok, mens siden hamrer løs på Shopify.

<Warning>
  **Kald aldrig en action ubetinget fra `cart_updated` eller `cart_loaded`.** Beskyt den med et tjek på den tilstand, du er ved at skabe, så anden gennemkørsel intet gør.
</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);
  }
});
```

Kurven giver dig ét sikkerhedsnet: en opdatering, der producerer en **identisk** kurv, udsender intet, så en genhentning, der intet ændrer, genstarter ikke cyklussen. Det beskytter dig mod utilsigtede no-op-løkker. Det beskytter dig **ikke** mod en handler, der reelt ændrer kurven hver gang.

<div id="treat-the-payload-as-read-only">
  ### Behandl payloaden som skrivebeskyttet
</div>

Hver handler for ét event modtager det *samme* objekt. At mutere det ændrer, hvad de handlers, der kommer efter din, ser, herunder handlers, der tilhører andre apps i butikken.

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

For faktisk at ændre kurven skal du bruge en [action](/da/aftersell/cart/sdk-actions). For at ændre, hvordan linjer renderes, skal du bruge [`registerLineTransform`](/da/aftersell/cart/sdk-hooks#registerlinetransform).

***

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

Affyres **én gang**, når kurven først indlæses på siden. Payloaden er det fulde [cart-objekt](/da/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');
});
```

**Brug det til:** alt der skal køre mod kurvens starttilstand, såsom at afstemme en gratis gave, initialisere en widget eller rapportere kurvindhold til analyse ved sideindlæsning.

**`cart_loaded` genafspilles for sene abonnenter.** Hvis du abonnerer, efter kurven allerede er indlæst, kaldes din handler straks med den aktuelle kurv. Abonnementsrækkefølge betyder aldrig noget, så du behøver ikke bekymre dig om, hvorvidt dit script nåede det før kurven.

<Tip>
  Logik, der skal være korrekt både ved sideindlæsning og ved hver ændring derefter, bør abonnere på **både** `cart_loaded` og `cart_updated` med den samme funktion. Det er standardmønsteret for "hold X synkroniseret med kurven".
</Tip>

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

Affyres, hver gang kurvens indhold ændres **efter** den første indlæsning, uanset om det er fra draweren, fra dine egne actions, fra temaet eller fra en anden app. Payloaden er det fulde [cart-objekt](/da/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);
});
```

**Brug det til:** at holde noget uden for kurven synkroniseret, såsom en brugerdefineret total, en fremdriftslinje, et header-badge eller et analyse-event ved hver ændring.

En opdatering, der producerer en identisk kurv, udsender intet. At genåbne draweren, skifte tilbage til fanen eller en genhentning, der returnerer det samme indhold, affyrer den ikke.

<Warning>
  Genlæs [de to regler](#the-two-rules), før du kalder en action herinde.
</Warning>

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

Affyres, når en **ny linje** dukker op i kurven. Payloaden er `{ item }`, hvor `item` er [kurvlinjen](/da/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,
  });
});
```

**Brug det til:** add to cart-tracking i et tredjeparts analyseværktøj. Dette er den absolut mest almindelige brug af SDK'et. Se [tracking af add to cart](/da/aftersell/cart/sdk-use-case-analytics).

To ting at vide om, hvordan det udledes:

<Warning>
  **En antalsændring er ikke en tilføjelse.** Kurven udregner tilføjelser og fjernelser ved at diffe *linjer*, ikke antal. En kunde, der ændrer en linje fra 1 til 3, affyrer `cart_updated`, ikke `item_added`. Hvis du også skal fange antalsstigninger, skal du sammenligne med den forrige tilstand i en `cart_updated`-handler.
</Warning>

Det affyres heller ikke for varer, der allerede var i kurven, da siden blev indlæst; de ankommer via `cart_loaded`. Tilføjes flere forskellige produkter på én gang, affyres eventet én gang pr. linje.

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

Affyres, når en linje forsvinder fra kurven. Payloaden er `{ item }`, linjen som den var, lige før den forsvandt, så du stadig kan læse dens `key`, `variantId` og `title`.

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

**Brug det til:** at tilbageføre noget, du gjorde ved tilføjelsen, såsom at rydde et flag, genvise et tilbud, kunden afslog, eller rapportere fjernelser til analyse.

Samme forbehold som `item_added`: at sænke et antal uden at ramme nul er ikke en fjernelse.

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

Affyres, når draweren åbner og lukker. Ingen 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');
});
```

**Brug dem til:** visningstracking, at pause en video eller karrusel bag draweren, at slå en klasse til/fra på siden.

Ingen af dem affyres ved den indledende sideindlæsning, kun ved en faktisk åbning eller lukning.

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

Affyres, når kunden klikker på checkout-knappen, umiddelbart før browseren navigerer. Ingen payload.

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

**Brug det til:** tracking af checkout-hensigt.

<Warning>
  **Du kan ikke annullere checkout fra denne handler.** Eventet er en notifikation, ikke en gate; navigationen sker, uanset hvad din kode gør. Hold handleren hurtig og synkron: et `await` eller et langsomt netværkskald når muligvis ikke at blive færdigt, før siden lukkes ned. Brug [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) til alt, du pålideligt skal have sendt.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## Lyt fra uden for SDK'et
</div>

Hvert event dispatches også som et DOM-`CustomEvent` på `window`, så du kan lytte uden at røre `window.aftersell.cart`. Det er nyttigt fra en temafil, en tredjepartsapp eller et script, der indlæses uafhængigt af kurven.

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

Bemærk navngivningen: bussen bruger `snake_case`, DOM-eventene bruger `kebab-case` bag et `aftersell:cart:`-præfiks.

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

Payloaden ankommer på `event.detail` og matcher [cart-objektet](/da/aftersell/cart/sdk-cart-object). Events dispatches på `window`, så en listener hvor som helst på siden modtager dem. Kurven renderes i en shadow root, men shadow-grænsen er aldrig i eventets sti. Hver dispatch kloner payloaden, så en listener, der muterer `event.detail`, ikke kan påvirke andre, og en listener, der kaster en fejl, ikke kan forstyrre SDK'et.

<Warning>
  **`cart-loaded` genafspilles ikke på DOM'en.** Bussen genafspiller `cart_loaded` for sene abonnenter, men den sti omgår DOM-dispatchen, så `window.addEventListener('aftersell:cart:cart-loaded')` registreret, efter kurven allerede er indlæst, affyres aldrig. Hvis dit scripts indlæsningsrækkefølge ikke er garanteret, skal du bruge `window.aftersell.cart.events.on('cart_loaded', …)`, som genafspiller, eller også lytte efter `aftersell:cart:cart-updated`.
</Warning>

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

Separat udgiver kurven Shopifys [standard-kurvevents](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) på `document`, hver gang den ændrer kurven, så temakode og andre apps kan reagere på Aftersells mutationer på samme måde, som de reagerer på temaets:

| Event                          | Payload på event-instansen                                                       |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `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>
  **Payloaden ligger ikke på `event.detail`.** `detail` bærer kun `{ source: 'aftersell' }` — det tag, kurven bruger til at ignorere sine egne events i stedet for at gå i løkke. Alt i tabellen ovenfor tildeles direkte på event-objektet, så læs `event.action`, ikke `event.detail.action`.
</Warning>

Hvert event bærer også et `promise`, som Aftersell afgør, når den underliggende skrivning lander, i overensstemmelse med Shopifys standard — await det, resolve det ikke. Disse dispatches på `document` og bobler, så en `window`-listener modtager dem også.

<div id="where-to-go-next">
  ## Hvor skal du hen nu
</div>

* **[Cart-objekt](/da/aftersell/cart/sdk-cart-object)**: den fulde form af payloadene ovenfor.
* **[Actions](/da/aftersell/cart/sdk-actions)**: hvordan du ændrer kurven fra en handler.
* **[Hooks](/da/aftersell/cart/sdk-hooks)**: til at ændre, hvordan kurven renderes, frem for at reagere på den.
* **[Use cases](/da/aftersell/cart/sdk-use-cases)**: analysetracking, gratis gaver og andre komplette eksempler.
