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

# Eventos

> Todos os eventos do SDK do Aftersell Cart: quando cada um dispara, o que entrega, para que usá-lo e os erros que causam loops infinitos.

Eventos permitem executar código **quando algo acontece** no carrinho. Eles ficam em `window.aftersell.cart.events`.

Assinar é uma chamada de configuração, então é seguro no topo do seu script, sem precisar esperar por `ready()`.

<div id="available-events">
  ## Eventos disponíveis
</div>

| Evento                                        | Payload                                               | Dispara quando                                             |
| --------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/pt/aftersell/cart/sdk-cart-object) | O carrinho carrega, uma vez por página.                    |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/pt/aftersell/cart/sdk-cart-object) | O conteúdo do carrinho muda, após o primeiro carregamento. |
| [`item_added`](#item_added)                   | `{ item }`                                            | Uma nova linha aparece no carrinho.                        |
| [`item_removed`](#item_removed)               | `{ item }`                                            | Uma linha desaparece do carrinho.                          |
| [`cart_opened`](#cart_opened-and-cart_closed) | Nenhum                                                | O drawer abre.                                             |
| [`cart_closed`](#cart_opened-and-cart_closed) | Nenhum                                                | O drawer fecha.                                            |
| [`checkout`](#checkout)                       | Nenhum                                                | O botão de checkout é clicado.                             |

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

`events.on(event, handler)` registra um handler e **retorna uma função que cancela a assinatura**:

```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)`: dispara uma vez e cancela a si mesmo.
* `events.off(event, handler)`: remove um handler específico.

Um handler que lança um erro é isolado e registrado no console; os outros handlers ainda são executados.

***

<div id="the-two-rules">
  ## As duas regras
</div>

Quase todo bug de evento tem origem em uma destas.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### Não altere o carrinho a partir de `cart_updated` sem uma proteção
</div>

Alterar o carrinho dentro de um handler de `cart_updated` dispara `cart_updated` de novo. Se esse handler alterar o carrinho novamente, você tem um loop infinito. O comprador vê o carrinho oscilando enquanto a página martela o Shopify.

<Warning>
  **Nunca chame uma ação incondicionalmente a partir de `cart_updated` ou `cart_loaded`.** Proteja-a com uma verificação do estado que você está prestes a criar, para que a segunda passagem não faça nada.
</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);
  }
});
```

O carrinho oferece uma rede de segurança: uma atualização que produz um carrinho **idêntico** não emite nada, então uma rebusca que não muda nada não reinicia o ciclo. Isso protege você de loops acidentais sem efeito. Mas **não** protege de um handler que genuinamente altera o carrinho a cada vez.

<div id="treat-the-payload-as-read-only">
  ### Trate o payload como somente leitura
</div>

Todo handler de um mesmo evento recebe o *mesmo* objeto. Mutá-lo muda o que os handlers depois do seu veem, incluindo handlers de outros apps na loja.

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

Para realmente alterar o carrinho, use uma [ação](/pt/aftersell/cart/sdk-actions). Para mudar como as linhas são renderizadas, use [`registerLineTransform`](/pt/aftersell/cart/sdk-hooks#registerlinetransform).

***

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

Dispara **uma vez**, quando o carrinho carrega pela primeira vez na página. O payload é o [objeto de carrinho](/pt/aftersell/cart/sdk-cart-object) completo.

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

**Use para:** qualquer coisa que precise rodar contra o estado inicial do carrinho, como reconciliar um brinde, inicializar um widget ou reportar o conteúdo do carrinho para analytics no carregamento da página.

**`cart_loaded` é reproduzido para assinantes atrasados.** Se você assinar depois que o carrinho já carregou, seu handler é chamado imediatamente com o carrinho atual. A ordem de assinatura nunca importa, então você não precisa se preocupar se o seu script chegou antes do carrinho.

<Tip>
  Lógica que precisa estar correta tanto no carregamento da página quanto em cada mudança posterior deve assinar **ambos** `cart_loaded` e `cart_updated` com a mesma função. Esse é o padrão para "manter X em sincronia com o carrinho".
</Tip>

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

Dispara toda vez que o conteúdo do carrinho muda **após** o primeiro carregamento, seja pelo drawer, pelas suas próprias ações, pelo tema ou por outro app. O payload é o [objeto de carrinho](/pt/aftersell/cart/sdk-cart-object) completo.

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

**Use para:** manter algo fora do carrinho em sincronia, como um total personalizado, uma barra de progresso, um badge no header ou um evento de analytics a cada mudança.

Uma atualização que produz um carrinho idêntico não emite nada. Reabrir o drawer, voltar para a aba ou uma rebusca que retorna o mesmo conteúdo não o disparam.

<Warning>
  Releia [as duas regras](#the-two-rules) antes de chamar uma ação aqui dentro.
</Warning>

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

Dispara quando uma **linha nova** aparece no carrinho. O payload é `{ item }`, onde `item` é a [linha do carrinho](/pt/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,
  });
});
```

**Use para:** rastreamento de adição ao carrinho em uma ferramenta de analytics de terceiros. Este é o uso mais comum do SDK. Veja [rastreando adições ao carrinho](/pt/aftersell/cart/sdk-use-case-analytics).

Duas coisas a saber sobre como ele é derivado:

<Warning>
  **Uma mudança de quantidade não é uma adição.** O carrinho detecta adições e remoções fazendo diff das *linhas*, não das quantidades. Um comprador subindo uma linha de 1 para 3 dispara `cart_updated`, não `item_added`. Se você precisa capturar aumentos de quantidade também, compare com o estado anterior em um handler de `cart_updated`.
</Warning>

Ele também não dispara para itens que já estavam no carrinho quando a página carregou; esses chegam via `cart_loaded`. Adicionar vários produtos distintos de uma vez dispara o evento uma vez por linha.

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

Dispara quando uma linha desaparece do carrinho. O payload é `{ item }`, a linha como estava logo antes de sumir, então você ainda pode ler seu `key`, `variantId` e `title`.

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

**Use para:** reverter algo que você fez na adição, como limpar uma flag, exibir novamente uma oferta que o comprador recusou ou reportar remoções para analytics.

Mesma ressalva de `item_added`: reduzir uma quantidade sem chegar a zero não é uma remoção.

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

Disparam quando o drawer abre e fecha. Sem 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');
});
```

**Use para:** rastreamento de visualizações, pausar um vídeo ou carrossel atrás do drawer, alternar uma classe na página.

Nenhum dos dois dispara no carregamento inicial da página, apenas em uma abertura ou fechamento real.

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

Dispara quando o comprador clica no botão de checkout, imediatamente antes de o navegador navegar. Sem payload.

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

**Use para:** rastreamento de intenção de checkout.

<Warning>
  **Você não pode cancelar o checkout a partir deste handler.** O evento é uma notificação, não um portão; a navegação acontece independentemente do que seu código faz. Mantenha o handler rápido e síncrono: um `await` ou uma chamada de rede lenta pode não terminar antes de a página descarregar. Use [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) para qualquer coisa que você precise enviar de forma confiável.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## Ouvindo de fora do SDK
</div>

Todo evento também é despachado como um `CustomEvent` do DOM em `window`, então você pode escutar sem tocar em `window.aftersell.cart`. Isso é útil em um arquivo de tema, um app de terceiros ou um script que carrega independentemente do carrinho.

| Evento do bus  | Evento do 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`     |

Atenção à nomenclatura: o bus usa `snake_case`, os eventos do DOM usam `kebab-case` atrás de um prefixo `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);
});
```

O payload chega em `event.detail` e corresponde ao [objeto de carrinho](/pt/aftersell/cart/sdk-cart-object). Os eventos são despachados em `window`, então um listener em qualquer lugar da página os recebe. O carrinho é renderizado em um shadow root, mas a fronteira do shadow nunca está no caminho do evento. Cada despacho clona o payload, então um listener que muta `event.detail` não afeta ninguém mais, e um listener que lança um erro não perturba o SDK.

<Warning>
  **`cart-loaded` não é reproduzido no DOM.** O bus reproduz `cart_loaded` para assinantes atrasados, mas esse caminho não passa pelo despacho no DOM, então `window.addEventListener('aftersell:cart:cart-loaded')` registrado depois que o carrinho já carregou nunca dispara. Se a ordem de carregamento do seu script não é garantida, use `window.aftersell.cart.events.on('cart_loaded', …)`, que reproduz, ou escute também `aftersell:cart:cart-updated`.
</Warning>

<div id="shopify-standard-cart-events">
  ### Eventos padrão de carrinho do Shopify
</div>

Separadamente, o carrinho publica os [eventos padrão de carrinho](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) do Shopify em `document` sempre que altera o carrinho, para que o código do tema e outros apps possam reagir às mutações do Aftersell da mesma forma que reagem às do tema:

| Evento                         | Payload na instância do evento                                                   |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `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>
  **O payload não está em `event.detail`.** `detail` carrega apenas `{ source: 'aftersell' }` — a tag que o carrinho usa para ignorar os próprios eventos em vez de entrar em loop. Tudo na tabela acima é atribuído diretamente ao objeto do evento, então leia `event.action`, não `event.detail.action`.
</Warning>

Cada evento também carrega uma `promise` que o Aftersell resolve quando a escrita subjacente é concluída, seguindo o padrão do Shopify — aguarde-a, não a resolva você. Eles são despachados em `document` e propagam (bubble), então um listener em `window` também os recebe.

<div id="where-to-go-next">
  ## Para onde ir agora
</div>

* **[Objeto de carrinho](/pt/aftersell/cart/sdk-cart-object)**: o formato completo dos payloads acima.
* **[Ações](/pt/aftersell/cart/sdk-actions)**: como alterar o carrinho a partir de um handler.
* **[Hooks](/pt/aftersell/cart/sdk-hooks)**: para mudar como o carrinho renderiza, em vez de reagir a ele.
* **[Casos de uso](/pt/aftersell/cart/sdk-use-cases)**: rastreamento de analytics, brindes e outros exemplos completos.
