Recalculating Button State

Ensure your button remains implemented properly when variants change on PDPs.

Background

Themes can impact the TryNow Button visibility unintentionally when the TryNow Button is unable to determine when things on the page have changed, or because things on the page re-render. For example, a user can switch from one variant to another which may trigger an event that would cause the TryNow button to "disappear" because the state of the TryNow Button was not recalculated, or because the section was re-rendered entirely causing state issues.

Before using this documentation, please ensure you have taken the necessary steps to enable and implement the TryNow Button. See a more comprehensive list of eligibility requirements here.

What's next?

In order to guarantee button visibility is being calculated on your products, we provide a method on window.trynow.product object recalculateCtaButtonState(PRODUCT_DATA).

Steps

  1. Locate the product.liquid template file.
  2. At the end of the file (or before the {% schema %} tag), add <script> tags.
  3. Inside the <script> tags, add the code to execute the function recalculateCtaButtonState(productData) each time the product changes. To maintain order, we'll add the code progressively.

The productData object should have the definition of below inputs. TryNow recommends comparing this to the product schema provided in Shopify documentation.

let productData = {
  id: null,
  title: "",
  handle: "",
  description: ``,
  published_at: "",
  created_at: "",
  vendor: "",
  type: "",
  tags: [],
  price: 0,
  price_min: 0,
  price_max: 0,
  available: true,
  price_varies: false,
  compare_at_price: null,
  compare_at_price_min: 0,
  compare_at_price_max: 0,
  compare_at_price_varies: false,
  variants: [],
  images: [],
  featured_image: "",
  options: [],
  media: [],
  requires_selling_plan: false,
  selling_plan_groups: [],
  content: ``
}

🚧

Heads up!

You have to use the entire product data object, but change the fields documented in recalculateCtaButtonstate()

  1. Create a function to build the productData object and also to execute the recalculateCtaButtonState(productData) function
function buildAndSendProductData() {
  // Get the current URL of the window
  const currentUrl = window.location.href;

  // Split the URL into segments using '/' as the delimiter
  const segments = currentUrl.split('/');

  // Find the index of the "products" segment in the URL path and add 1 to get the index of the product handle
  const productHandleIndex = segments.findIndex(segment => segment === "products") + 1;

  // Retrieve the product handle from the segments array using the productHandleIndex
  const productHandle = segments[productHandleIndex];

  // Remove any query parameters from the product handle by splitting it at '?' and taking the first part
  const cleanProductHandle = productHandle.split('?')[0];

  fetch(`/products/${cleanProductHandle}.json`)
    .then(response => response.json())
    .then(data => {
      productData.id = data.product.id;

      data.product.variants.forEach(variant => {
        productData.variants.push({
          available: true,
          barcode: variant.barcode,
          compare_at_price: variant.compare_at_price,
          featured_image: {},
          featured_media: {},
          id: variant.id,
          inventory_management: variant.inventory_management,
          name: '',
          option1: variant.option1,
          option2: variant.option2,
          option3: null,
          options: [],
          price: 0,
          public_title: "",
          quantity_rule: variant.quantity_rule,
          requires_selling_plan: false,
          requires_shipping: variant.require_shipping,
          selling_plan_allocations: [
            {
              "price_adjustments": [],
              "price": 0,
              "compare_at_price": 0,
              "per_delivery_price": 0,
              "selling_plan_id": [SELLING_PLAN_ID],
              "selling_plan_group_id": ""
            }
          ],
          sku: variant.sku,
          taxable: variant.taxable,
          title: variant.title,
          weight: variant.weight
        });
      });

      productData.tags = data.product.tags.split(", ");

      if (window.trynow && typeof window.trynow.product.recalculateCtaButtonState === 'function') {
        window.trynow.product.recalculateCtaButtonState(productData);
      } else {
        console.error('recalculateCtaButtonState function is not defined.');
      }
    })
    .catch(error => {
      console.error('Error fetching product data:', error);
    });
}

🚧

Heads up!

In this example, an AJAX call is used to retrieve the product information. However it's important to note that depending on your Shopify configuration some information may not be accessible via an AJAX call.

📘

Hint

Remember to replace the placeholder [SELLING_PLAN_ID] with your actual TryNow Selling Plan ID. This value should be treated as an integer. To indetify the plan ID you can query the contents of the TryNow selling plan meta tag in your browser: document.querySelector('meta[name="trynow:selling_plan_id"]').content

  1. Finally, to execute the new function when the product changes, identify the variant selection inputs and add a click event listener to them.
document.addEventListener('DOMContentLoaded', function(){
  let productOptions = document.querySelectorAll('[VARIANT_INPUTS_SELECTOR]')
  productOptions.forEach(option => {
    option.addEventListener('click', function(){
      setTimeout(function(){
        buildAndSendProductData()
      },500)
    })
  })
})

🚧

Heads up!

Remember to replace the placeholder [VARIANT_INPUTS_SELECTOR] with the correct selector. In some cases there can be multiple variant selections.

If all the steps have been followed correctly, the code should look like this, with the necessary modifications according to your needs:

<script>
  let productData = {
    id: null,
    title: "",
    handle: "",
    description: ``,
    published_at: "",
    created_at: "",
    vendor: "",
    type: "",
    tags: [],
    price: 0,
    price_min: 0,
    price_max: 0,
    available: true,
    price_varies: false,
    compare_at_price: null,
    compare_at_price_min: 0,
    compare_at_price_max: 0,
    compare_at_price_varies: false,
    variants: [],
    images: [],
    featured_image: "",
    options: [],
    media: [],
    requires_selling_plan: false,
    selling_plan_groups: [],
    content: ``
  }
  
  function buildAndSendProductData() {
    // Get the current URL of the window
    const currentUrl = window.location.href;

    // Split the URL into segments using '/' as the delimiter
    const segments = currentUrl.split('/');

    // Find the index of the "products" segment in the URL path and add 1 to get the index of the product handle
    const productHandleIndex = segments.findIndex(segment => segment === "products") + 1;

    // Retrieve the product handle from the segments array using the productHandleIndex
    const productHandle = segments[productHandleIndex];

    // Remove any query parameters from the product handle by splitting it at '?' and taking the first part
    const cleanProductHandle = productHandle.split('?')[0];

    fetch(`/products/${cleanProductHandle}.json`)
      .then(response => response.json())
      .then(data => {
        productData.id = data.product.id;

        data.product.variants.forEach(variant => {
          productData.variants.push({
            available: true,
            barcode: variant.barcode,
            compare_at_price: variant.compare_at_price,
            featured_image: {},
            featured_media: {},
            id: variant.id,
            inventory_management: variant.inventory_management,
            name: '',
            option1: variant.option1,
            option2: variant.option2,
            option3: null,
            options: [],
            price: 0,
            public_title: "",
            quantity_rule: variant.quantity_rule,
            requires_selling_plan: false,
            requires_shipping: variant.require_shipping,
            selling_plan_allocations: [
              {
                "price_adjustments": [],
                "price": 0,
                "compare_at_price": 0,
                "per_delivery_price": 0,
                "selling_plan_id": [SELLING_PLAN_ID],
                "selling_plan_group_id": ""
              }
            ],
            sku: variant.sku,
            taxable: variant.taxable,
            title: variant.title,
            weight: variant.weight
          });
        });

        productData.tags = data.product.tags.split(", ");

        if (window.trynow && typeof window.trynow.product.recalculateCtaButtonState === 'function') {
          window.trynow.product.recalculateCtaButtonState(productData);
        } else {
          console.error('recalculateCtaButtonState function is not defined.');
        }
      })
      .catch(error => {
        console.error('Error fetching product data:', error);
      });
  }

  document.addEventListener('DOMContentLoaded', function(){
    let productOptions = document.querySelectorAll('[VARIANT_INPUTS_SELECTOR]')
    productOptions.forEach(option => {
      option.addEventListener('click', function(){
        setTimeout(function(){
          buildAndSendProductData()
        },500)
      })
    })
  })
</script>