# Buy X get Y events

> DOM events from the Buy X get Y banner and the andor:offer_* customer events you can read in a Shopify custom pixel, plus its data attributes and states.

Source: https://backtrackhq.com/docs/widgets/buy-x-get-y/events/

This page is for theme developers. The Buy X get Y banner host,
`<andor-bundle data-andor-widget="bxgy">`, sends the shared widget
events. The banner and the cart claim row also publish upsell offer
events for custom pixels.

All DOM events are `CustomEvent`s that bubble, so you can listen on
`document`. The `andor:offer_*` events are not DOM events. Subscribe to
them in a custom pixel.

> **Note:** A custom pixel is optional. AndOr counts offers without
> one. Add a pixel only if you want the `andor:offer_*` events in your
> own tools.

## Events

| Event | Sent on | When |
|---|---|---|
| `andor:widget:mount` | the banner | the first pass that sees it |
| `andor:widget:unmount` | the banner and `document` | the first pass after it left the page |
| `andor:display:change` | the banner | its state changed, for example it hid or showed again |
| `andor:offer_viewed` | customer events | the offer is first seen, once per offer and placement per visit |
| `andor:offer_clicked` | customer events | a shopper presses **Claim** |
| `andor:offer_added` | customer events | the claimed item was added to the cart |

Rules worth knowing:

- **No event on first paint.** `andor:display:change` fires only when
  the state really changed.
- **Up to one second late.** Treat a change event as a notice, not as
  the exact moment.
- **The cart claim row is not a widget host.** It sends no DOM events,
  only the `andor:offer_*` events.

## Payloads

`andor:widget:mount`, `andor:widget:unmount` and
`andor:display:change` carry `event.detail.resource`:

| Field | Value for this widget |
|---|---|
| `widgetType` | `"bxgy"` |
| `widgetId` | the block id, or `"pasted"` for a pasted tag |
| `productId` | `null` (the banner carries no product id) |

`andor:display:change` adds `previous` and `current` state objects
(`previous` is `null` on the first change after mount). The banner
carries no discount, kind, mode, date or price attributes, so the useful
field is `visible`:

| State field | Value for this widget |
|---|---|
| `visible` | `true` while the banner shows |
| `discountId`, `kind`, `mode`, `endsAt` | `null` |
| `priceCents`, `originalCents` | `null` |

The `andor:offer_*` events carry `event.customData`:

| Field | Type | Meaning |
|---|---|---|
| `offerId` | string | the offer id (digits) |
| `placement` | string | `"product_page"` (banner), `"cart_page"` or `"cart_drawer"` (claim row) |
| `valueCents` | number | adds only: the value of what was added (full price times units), in cents |

No customer data, cart token or product ids are sent.

## Examples

React when the banner hides:

```js
document.addEventListener("andor:display:change", (event) => {
  const { widgetType, previous, current } = event.detail.resource;
  if (widgetType !== "bxgy") return;
  if (previous?.visible && !current.visible) {
    console.log("Buy X get Y banner hid");
  }
});
```

Optional: count claims in a custom pixel:

```js
// Shopify admin: Settings > Customer events > Add custom pixel
analytics.subscribe("andor:offer_added", (event) => {
  const { offerId, placement, valueCents } = event.customData;
  console.log("upsell add", offerId, placement, valueCents);
});
```

## Events the widget listens for

The banner and claim row redraw when your theme sends these Shopify
events, so a theme that sends them gets the redraw for free:

- `shopify:cart:lines-update`: the cart changed. The widget reads it
  again.
- `shopify:cart:view`: the cart drawer opened.
- `shopify:section:load`: the theme editor re-rendered a section.

If you swap product markup with your own Ajax code, call
`AndOrDisplay.refresh()` and `AndOrEvents.refresh()` afterwards. See
[JavaScript API](https://backtrackhq.com/docs/developers/javascript-api/).

## Data attributes and states

### Product page banner

The banner uses the same attributes as the bundle card. See
[Bundle card events](https://backtrackhq.com/docs/widgets/bundle/events/#data-attributes-and-states).

### Cart claim rows

| Attribute or class | On | Meaning |
|---|---|---|
| `data-andor-state="unlocked"` | box | the claim row: X is met |
| `data-andor-state="off"` | box | claimed, another discount won |
| `data-andor-ready` | host | drawn from the cart. Before that, boxes shimmer |
| `data-andor-place` | host | `cart_page` or `cart_drawer` |

Inside the claim host, the boxes use `data-andor-state` for their own
progress, because no other script writes it there.

For the attributes every widget shares, like `data-andor-state` and
`data-andor-progress`, see
[Data attributes and states](https://backtrackhq.com/docs/developers/data-attributes/).
