# Events

{% hint style="info" %}
The documentation below describes the API as of version 9.x of the script.&#x20;

Public properties or functions that are not in the `discountNinja.api` namespace should be considered obsolete and will be removed in the next major version release.

Using undocumented functions or objects will result in issues as custom code that relies on those functions or objects may break in the future when Discount Ninja's script is automatically updated.
{% endhint %}

## API ready

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:api:ready</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Signals that the script has loaded and interaction with the API is possible. </td></tr></tbody></table>

#### Syntax

```javascript
function performActionWhenApiIsReady() {
    //Add your logic here
    console.log('API ready');
}

//Check if the API is ready
if (typeof discountNinja === 'undefined' || 
    typeof discountNinja.api === 'undefined') {
    //The API is not ready, wait for it
    document.addEventListener("la:dn:api:ready", function(event) { 
        performActionWhenApiIsReady();
    });
}
else {
    //The API was already available, no need to wait
    performActionWhenApiIsReady();
}
```

## Promotions loaded

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:promotions:loaded</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published after promotions have been loaded, processed and filtered by the app. It allows the consumer to inspect which promotions are available for the user during the browsing session. </td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains a promotions object.</td></tr></tbody></table>

#### Syntax

```javascript
document.addEventListener("la:dn:promotions:loaded", function(event) { 
    const promotions = event.detail.data[0]; 
    console.log('Promotions loaded', promotions);
});
```

## Product discount calculated

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:product:discount:calculated</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>This event is published when the (discounted) price of any marked product on the page has been calculated.</td></tr><tr><td></td><td>The app will attempt to find all products on a page based on markers in the HTML. It then applies the available promotions to those products and publishes one event per product variant with the results.</td></tr><tr><td></td><td>For the current product, this event is published when the page is loaded and is published again when the selected variant or selected selling plan changes.<br>For other products, this event is published only once per product and per page load. </td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains a <a href="../objects#productvariantpriceinfo">product variant price info</a> object.</td></tr></tbody></table>

#### Syntax

<pre class="language-javascript"><code class="lang-javascript"><strong>document.addEventListener("la:dn:product:discount:calculated", function(event) {
</strong>    const productVariantPriceInfo = event.detail.data[0]; 
    console.log('Product variant price calculated', productVariantPriceInfo);
});
</code></pre>

## Cart updated

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:cart:updated</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when a change to the cart has been detected and promotions have been evaluated. </td></tr><tr><td></td><td>This event is published when the page is loaded and is published again when the content of the cart changes or the set of applied promotions changes.</td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains a <a href="../objects#discounted-cart">discounted cart</a> object.</td></tr></tbody></table>

#### Syntax

```javascript
document.addEventListener("la:dn:cart:updated", function(event) {
    const discountedCart = event.detail.data[0]; 
    console.log('Discounted cart available', discountedCart); 
});
```

## Cart discounted

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:cart:discounted</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when a discount has been applied to the cart. </td></tr><tr><td></td><td>This event is published when the page is loaded and is published again when the content of the cart changes or the set of applied promotions changes.</td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains a <a href="../objects#discounted-cart">discounted cart</a> object.</td></tr></tbody></table>

#### Syntax

```javascript
document.addEventListener("la:dn:cart:discounted", function(event) {
    const discountedCart = event.detail.data[0]; 
    console.log('Discount applied to the cart', discountedCart); 
});
```

## Cart mutations processing

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:cart-mutations:processing</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when the store is making updates to the cart.</td></tr><tr><td></td><td>This event is published when Discount Ninja detects that network requests have been made to clear, update, change or add to the cart. Specifically, the app detects fetch requests to the /cart/add.js, /cart/update.js, /cart/change.js and /cart/clear.js (see <a href="https://shopify.dev/docs/api/ajax/reference/cart">https://shopify.dev/docs/api/ajax/reference/cart</a>).</td></tr></tbody></table>

#### Syntax

```javascript
// Add this HTML element to your (drawer) cart Liquid template
// It can be placed, for example, near the checkout button
<div id="cart-mutations-processing-label" style="display:none">
    Processing cart changes...
</div>

// Add this code to your theme.liquid in the body 
<script>
    document.addEventListener("la:dn:cart-mutations:processing", function() {
        console.log('Cart mutations are processing...'); 
        
        // Optionally, show an HTML element 
        // The HTML element can be used to communicate this process to the visitor
        const cartMutationsProcessingLabel = document.getElementById("cart-mutations-processing-label");
        if (cartMutationsProcessingLabel) cartMutationsProcessingLabel.style.display = 'block'
    });
</script>
```

## Cart mutations processed

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:cart-mutations:processed</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when the store has finished making updates to the cart.</td></tr><tr><td></td><td>This event is published when Discount Ninja detects that all network requests to the /cart/add.js, /cart/update.js, /cart/change.js and /cart/clear.js (see <a href="https://shopify.dev/docs/api/ajax/reference/cart">https://shopify.dev/docs/api/ajax/reference/cart</a>) have been completed.</td></tr></tbody></table>

#### Syntax

```javascript
// Add this HTML element to your (drawer) cart Liquid template
// It can be placed, for example, near the checkout button
<div id="cart-mutations-processing-label" style="display:none">
    Processing cart changes...
</div>

// Add this code to your theme.liquid in the body 
<script>
    document.addEventListener("la:dn:cart-mutations:processed", function() {
        console.log('Cart mutations are processed...'); 
        
        // Optionally, hide an HTML element 
        // The HTML element can be used to communicate this process to the visitor
        const cartMutationsProcessingLabel = document.getElementById("cart-mutations-processing-label");
        if (cartMutationsProcessingLabel) cartMutationsProcessingLabel.style.display = 'none';
    });
</script>
```

## Trigger checkout

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:checkout:initiate</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is subscribed to by the app.<br>To publish it, use the following syntax: </td></tr><tr><td></td><td><pre class="language-javascript"><code class="lang-javascript">document.dispatchEvent(new CustomEvent('la:dn:checkout:initiate'));
</code></pre></td></tr><tr><td><strong>Behavior</strong></td><td><p>This approach is useful if you need to trigger checkout programmatically without bypassing Discount Ninja.</p><p></p><p>Note: if you need a custom checkout pipeline (for example, you may want to execute custom validation logic before allowing the checkout to proceed) when clicking the checkout button, use <a href="../functions#add-pipeline-rule">this API function</a> instead. </p></td></tr></tbody></table>

#### Syntax

```html
/// The markup below shows a custom button for illustration purposes
/// (i.e. not a button of type="submit" with name="checkout")
/// Note: we advise against using a custom checkout button
<button name="customcheckout" onclick="handleCustomCheckout()">
  Check out
</button>
```

```javascript
/// This function should be called by a custom checkout button 
/// such as the one described above
function handleCustomCheckout() {
  if (canAdvanceToCheckout() === true) {
     // Checks if Discount Ninja is available 
     if (discountNinja) {
        // Check if a Discount Ninja offer is applied to the cart        
        discountNinja.api.checkout.isDiscounted().then(function(requiresAdvancedCheckout) {
          if (requiresAdvancedCheckout === true) {
            // DN needs to handle the checkout
            advanceToCheckoutUsingDiscountNinja():
          }
          else {
            handleNonDiscountNinjaCheckout();
          }
        }); 
     }
     else {
        handleNonDiscountNinjaCheckout();
     }
  }
  else {
     //Custom logic to explain why the user cannot advance
  }
}

/// This optional function returns true if the user 
/// can advance to the checkout
function canAdvanceToCheckout() {
  return true;
}

/// This function instructs Discount Ninja
/// to advance to the checkout and apply offers
function advanceToCheckoutUsingDiscountNinja() {
  document.dispatchEvent(new CustomEvent('la:dn:checkout:initiate'));
}

/// This function should redirect the user to the checkout
/// when a checkout with Discount Ninja is not required
function handleNonDiscountNinjaCheckout() {
  //Custom logic to handle checkout
}
```

## Redirect to checkout

Discount Ninja expects users to transfer from the cart to the checkout using a checkout button or link. See this article for more information: <https://support.discountninja.io/en/articles/3505661-discount-ninja-checkout-button>

As a result, any code that redirects the user to the checkout using `window.location` is not supported. The following example shows what such an implementation would look like:

```html
/// The markup below shows a custom button for illustration purposes
/// (i.e. not a button of type="submit" with name="checkout")
/// Note: we advise against using a custom checkout button
<button name="customcheckout" onclick="handleCustomCheckout()">
  Check out
</button>
```

```javascript
/// This function is called by a custom checkout button 
function handleCustomCheckout() {
  window.location = "/checkout";
}
```

Redirecting a user to the checkout by setting the `window.location` causes problems with Discount Ninja. Additionally, it causes other integration issues for the merchant since redirecting without submitting the form has numerous flaws:

* The order note field is not submitted if it was changed during this session (and not updated using cart.js)
* The locale may not be passed
* The discount code present in the cart form is not passed
* ...

To resolve this, either use a standard checkout button (a button of type="submit" with name="checkout") or change your checkout logic as follows:

```javascript
/// This function is called by a custom checkout button 
function handleCustomCheckout() {
  if (discountNinja) {
    document.dispatchEvent(new CustomEvent('la:dn:checkout:initiate'));
  }
  else {
    // Your fallback logic
    // Note: we do not recommend redirecting to the checkout this way
    //       (see documentation), preferably resubmit the cart form
    window.location = window.Shopify.routes.root + "checkout";
  }
}
```

## Convert money fields

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:money:convert</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is subscribed to by the app.<br>To publish it, use the syntax explained in the example below. </td></tr><tr><td><strong>Behavior</strong></td><td>Most currency conversion mechanisms are supported out-of-the-box by Discount Ninja. This means there is no need to implement custom logic in most cases.</td></tr><tr><td></td><td>In case the currency conversion mechanism of your theme (or supporting app) is not automatically triggered, you can publish this event after the theme has updated prices to let Discount Ninja know that it should re-render prices.</td></tr></tbody></table>

#### Syntax

```javascript
function test() {
    // Custom logic that overrides prices
    document.dispatchEvent(new CustomEvent('la:dn:money:convert'));
}
```

## Collection products updated

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:collection:updated</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is subscribed to by the app.<br>To publish it, use the syntax explained in the example below. </td></tr><tr><td><strong>Behavior</strong></td><td>When collection products are added, removed or updated the app must recalculate the discounted prices and render the price.</td></tr><tr><td></td><td>Most mechanisms used to update collections are supported out-of-the-box by Discount Ninja. This means there is no need to implement custom logic in most cases.</td></tr><tr><td></td><td>In case the app is not automatically detecting a situation where collection products are updated, you can publish this event after the theme has updated the products to let Discount Ninja know that it should re-render prices.</td></tr></tbody></table>

#### Syntax

```javascript
function test() {
    // Custom logic that updates collection products
    document.dispatchEvent(new CustomEvent('la:dn:collection:updated'));
}
```

## Product changed

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:product:changed</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is subscribed to by the app.<br>To publish it, use the syntax explained in the example below. </td></tr><tr><td><strong>Behavior</strong></td><td>When a product page is rendered, the app calculates the discounted price for the selected variant of that product. If the same product page is re-used to render a different product (without navigating or reloading the page), this event can be used to inform Discount Ninja of that.</td></tr><tr><td></td><td>This event is only required in unusual cases where the same product page is used to render the product details of different products (usually variations of the same product that are not modeled as product variants).</td></tr><tr><td><strong>Parameters</strong></td><td><code>detail.data.product.id (number)</code>: the id of the product<br><code>detail.data.product.handle (string)</code>: the handle of the product</td></tr></tbody></table>

#### Syntax

```javascript
function test() {
    // Custom logic that informs Discount Ninja that a different
    // product is rendered on the product page
    const productId = 123456;
    const productHandle = "my-product";
    document.dispatchEvent(new CustomEvent("la:dn:product:changed", { 
        detail: { 
            data: { 
                product: { 
                    id: productId,
                    handle: productHandle
                } 
            } 
        } 
    }));
}
```

## Variant changed

#### Details

<table data-header-hidden><thead><tr><th width="179"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:variant:changed</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is subscribed to by the app.<br>To publish it, use the syntax explained in the example below. </td></tr><tr><td><strong>Behavior</strong></td><td>When a different variant is selected, the app must recalculate the discounted price and render the price.</td></tr><tr><td></td><td>Most mechanisms used to change variants are supported out-of-the-box by Discount Ninja. This means there is no need to implement custom logic in most cases.</td></tr><tr><td></td><td>In case the app is not automatically detecting a situation where the product variant is changed, you can publish this event to let Discount Ninja know that it should re-render prices.</td></tr><tr><td><strong>Parameters</strong></td><td><code>detail.data.variant.id (number)</code>: the id of the selected variant</td></tr></tbody></table>

#### Syntax

```javascript
function test() {
    // Custom logic that changes the selected variant
    const variantId = 123456;
    document.dispatchEvent(new CustomEvent("la:dn:variant:changed", { 
        detail: { 
            data: { 
                variant: { 
                    id: variantId 
                } 
            } 
        } 
    }));
}
```

## Drawer cart opened

#### Details

<table data-header-hidden><thead><tr><th width="157"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:drawercart:opened</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is subscribed to by the app.<br>To publish it, use the following syntax: </td></tr><tr><td></td><td><pre class="language-javascript"><code class="lang-javascript">document.dispatchEvent(new CustomEvent('la:dn:drawercart:opened'));
</code></pre></td></tr><tr><td><strong>Behavior</strong></td><td>When a drawer cart is opened the app may need to render discounted prices.</td></tr><tr><td></td><td>In case the app is not automatically detecting a situation where the drawer cart is opened, you can publish this event to let Discount Ninja know that it should re-render the line item prices and subtotal in the drawer cart.</td></tr></tbody></table>

#### Syntax

```javascript
function test() {
    //Custom logic that opens the drawer cart
    //And renders the content (including prices)
    document.dispatchEvent(new CustomEvent('la:dn:drawercart:opened'));
}
```

## Entitlements calculated

#### Details

<table data-header-hidden><thead><tr><th width="157"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:entitlements:calculated</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when the (discounted) cart and the associated entitlements have been calculated. </td></tr><tr><td></td><td>This event is published when the page is loaded and is published again when the content of the cart changes or the set of applied promotions changes.</td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains an array with <a href="../objects#entitlement-info">Entitlement info</a> objects. Each object documents if the user is entitled to a gift for a gift offer.</td></tr></tbody></table>

#### Syntax

```javascript
document.addEventListener("la:dn:entitlements:calculated", function(event) {
    const entitlements = event.detail.data[0]; 
    console.log('Entitlements calculated', entitlements); 
});
```

## Promotion code added

#### Details

<table data-header-hidden><thead><tr><th width="157"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:promotioncode:added</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when the a promotion code is redeemed using the promotion code field. </td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains:<br>- <code>promotionCode</code>: the promotion code that is applied<br>- <code>success</code>: a boolean indicating that the code was added successfully<br>- <code>manual</code>: a boolean indicating if the code was added manually or by the system</td></tr></tbody></table>

#### Syntax

```javascript
document.addEventListener("la:dn:promotioncode:added", function(event) {
    const eventData = event.detail.data[0]; 
    console.log('Promotion code added', eventData); 
});
```

## Promotion code removed

#### Details

<table data-header-hidden><thead><tr><th width="157"></th><th></th></tr></thead><tbody><tr><td><strong>Event</strong></td><td><code>la:dn:promotioncode:removed</code></td></tr><tr><td><strong>Direction</strong></td><td>This event is published by the app. You can optionally subscribe to it.</td></tr><tr><td><strong>Behavior</strong></td><td>Published when the a promotion code is removed using the promotion code field. </td></tr><tr><td><strong>Event properties</strong></td><td>The event data contains:<br>- <code>promotionCode</code>: the promotion code that is removed<br>- <code>success</code>: a boolean indicating that the code was removed successfully</td></tr></tbody></table>

#### Syntax

```javascript
document.addEventListener("la:dn:promotioncode:removed", function(event) {
    const eventData = event.detail.data[0]; 
    console.log('Promotion code removed', eventData); 
})
```
