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
- Locate the
product.liquid
template file. - At the end of the file (or before the
{% schema %}
tag), add<script>
tags. - Inside the
<script>
tags, add the code to execute the functionrecalculateCtaButtonState(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()
- 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
- 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>
Updated 22 days ago