---
title: JavaScript
description: The browser events CartPops V2 listens to and dispatches, and how to open the drawer or keep a counter in sync from your own code.
sidebar:
  label: JavaScript
  icon: braces
---

CartPops V2 has no JavaScript object and no methods to call. Its browser surface is a small set of DOM events on `document`, plus the standard WooCommerce events it listens to.

:::warning[What V2 does not have]
- There is no `window.CartPops` object. Code that calls `CartPops.drawer.show()` throws an error.
- There are no opened or closed events. CartPops tells you only when the item count changes.
- The `cartpops` Interactivity store exists, but only script modules can import it and it is not a supported API. Use the events below.
:::

## Events you can dispatch

Dispatch these on `document`. They take no data.

| Event | Effect |
| --- | --- |
| `cartpops:open` | Opens the drawer. If it is already open, it refreshes the cart data. |
| `cartpops:close` | Closes the drawer. |
| `cartpops:toggle` | Opens the drawer if it is closed, and closes it if it is open. |

The drawer listens from the moment its script module has run. The module loads after the page's classic scripts, so an event dispatched during page load, before the module runs, is lost. Dispatch from a click or other user action, or wait for `DOMContentLoaded`. The events do nothing if CartPops is switched off or the drawer is not on the page.

When the drawer opens, it reads the cart again, unless the cart data was refreshed in the last ten seconds.

### Example: open the drawer from your own button

```html
<button type="button" id="my-cart-button">Cart</button>
```

```js my-script.js
document.addEventListener( 'click', ( event ) => {
	const button = event.target.closest( '#my-cart-button' );
	if ( ! button ) {
		return;
	}

	event.preventDefault();
	document.dispatchEvent( new CustomEvent( 'cartpops:open' ) );
} );
```

The listener is on `document`, so it also works for buttons that your theme adds later. When the drawer closes, focus returns to the element that was focused when it opened, which is your button here.

:::note
In V1, adding the class `cpops-toggle-drawer` to any element opened the drawer. In V2 the class only styles CartPops' own launcher button. Use a click handler like the one above.
:::

## Event you can listen to

### `cartpops:count-updated`

CartPops dispatches this on `document` after the cart item count changes. The count is the total quantity of all cart lines, as an integer.

```js
document.addEventListener( 'cartpops:count-updated', ( event ) => {
	console.log( event.detail.count ); // For example 3.
} );
```

| Property | Type | Description |
| --- | --- | --- |
| `event.detail.count` | `number` | Total quantity in the cart after the change. |

Things to know:

- Removing a line fires the event straight away. Changing a quantity with the plus and minus buttons fires it after CartPops has confirmed the change with the server, which is a fraction of a second later.
- It can fire more than once with the same value. Do your own check if that matters.
- It does not fire at page load unless CartPops has to read the cart because the page may be out of date, for example on a cached page. Take the first value from your own markup or from the WooCommerce `woocommerce_items_in_cart` cookie.

### Example: keep a header counter in sync

```js my-script.js
document.addEventListener( 'cartpops:count-updated', ( event ) => {
	const count = Number( event.detail?.count ?? 0 );

	document.querySelectorAll( '[data-header-cart-count]' ).forEach( ( element ) => {
		element.textContent = String( count );
		element.hidden = count === 0;
	} );
} );
```

Give your counter element the attribute `data-header-cart-count` (or any selector you prefer). CartPops uses the same event to update the Blocksy header badge and its own Pro shipping meter.

## WooCommerce events CartPops listens to

CartPops keeps itself in step with the rest of the store by listening to these events. You do not need to dispatch them, but you can use them to tell CartPops that the cart changed.

| Event | Source | What CartPops does |
| --- | --- | --- |
| `added_to_cart` | jQuery, on `document.body`. Arguments: fragments, cart hash, button. | Reads the CartPops cart state from the `.cartpops-cart-json` fragment, so it needs no extra request. If the fragment is missing it reads the cart. Opens the drawer if **Open drawer on** is **Add to cart** and the button does not opt out. |
| `removed_from_cart` | jQuery, on `document.body`. | Updates the cart without opening the drawer. |
| `updated_wc_div` | jQuery, on `document.body`. | Reads the cart again. |
| `wc_fragments_refreshed` | jQuery, on `document.body`. | Reads the cart again. |
| `wc-blocks_added_to_cart` | DOM event on `document.body`, from WooCommerce blocks. | Reads the cart from the WooCommerce cart store. Opens the drawer if **Open drawer on** is **Add to cart**. |
| `wc-blocks_removed_from_cart` | DOM event on `document.body`. | Updates the cart without opening the drawer. |
| `wc/store/cart` | A subscription to the WooCommerce `wp.data` store. | Applies cart changes that WooCommerce blocks make, such as quantity, coupon or shipping changes. Does not open the drawer. |

CartPops also takes over clicks on the WooCommerce Mini-Cart block button when **Integration mode** under **WooCommerce Mini Cart** (Advanced > General) is set to **Replace**.

On a single product page, **Add to cart on product pages without reloading** (Advanced > General) makes CartPops submit the classic `form.cart` in the background. It listens for `submit` on `window`, so your handlers on the form or document run first. If one calls `event.preventDefault()`, CartPops leaves the form alone. After a confirmed add it fires `added_to_cart` with refreshed fragments and the clicked button, so the per-button opt-out below works on the product page too.

### jQuery must load first

CartPops attaches the jQuery events (`added_to_cart`, `removed_from_cart`, `updated_wc_div` and `wc_fragments_refreshed`) only if jQuery already exists when its script runs. If a speed plugin delays or defers jQuery, CartPops never hears these events. The `wc-blocks_*` events are plain DOM events and do not need jQuery.

### Stop one button from opening the drawer

Add `data-cpops-cart-open="false"` to an AJAX add to cart button to add the product without opening the drawer.

```html
<a href="?add-to-cart=123" data-product_id="123" data-cpops-cart-open="false" class="button add_to_cart_button ajax_add_to_cart">
	Add to cart
</a>
```

This works for classic AJAX adds, where WooCommerce fires `added_to_cart` with the button. It does not apply to adds made by WooCommerce blocks. To stop opening on every add, set **Open drawer on** to **Launcher click only**, or use the [`cartpops_add_to_cart_trigger` filter](/developers/php-filters#cartpops_add_to_cart_trigger).

### Example: open the drawer after your own AJAX add

If your own code adds a product with WooCommerce's AJAX add to cart request, trigger the standard `added_to_cart` event with the response. CartPops then updates from the response and opens the drawer, following the **Open drawer on** setting.

```js my-script.js
// `data` is the JSON response from WooCommerce's add_to_cart AJAX request.
function announceAddedToCart( data, button ) {
	window.jQuery( document.body ).trigger( 'added_to_cart', [
		data.fragments,
		data.cart_hash,
		window.jQuery( button ),
	] );
}
```

If you add to the cart some other way and only want the drawer to show, dispatch `cartpops:open`. If you changed the cart less than ten seconds after the drawer last refreshed its data, the drawer can briefly show the earlier state.

## What CartPops dispatches for WooCommerce

After CartPops changes the cart itself, for example a quantity change, a coupon, or a recommendation add, it asks WooCommerce to refresh the other cart widgets. It does this once, about 50 milliseconds after the last change:

- It triggers the jQuery `wc_fragment_refresh` event on `document.body`, if jQuery is present.
- It asks the WooCommerce `wc/store/cart` store to load the cart again.

CartPops does not listen to `wc_fragment_refresh` itself.

CartPops does not fire `added_to_cart`, `removed_from_cart` or `adding_to_cart` for the adds and removals it makes, such as a recommendation add. Analytics plugins that rely on these events will not see those actions.

## Not public

These exist in the code but are internal, and can change without notice: the `cartpops` Interactivity store and its actions, the global keys `Symbol.for('cartpops.cartDrawerEventBridge')` and `Symbol.for('cartpops.wooCartSurfaceSync')`, and the `window.wp.interactivity` global, which WordPress does not guarantee.

The drawer's DOM does show its state: while it is open, `.cpops-drawer` has the class `cpops-drawer-open`. You can watch that class if you really need to know, but this is rendered markup, not a promised API, and CartPops sends no event when it changes.

:::note[Coming from V1?]
`window.CartPops`, `drawer.show()`, `drawer.on('show')` and the popup, bar and assistant objects are gone. See [Upgrading to V2: for developers](/upgrading-to-v2/developers) for replacements.
:::
