Hi Community, We were unable to dynamically change the ISBN of a product variant when a different variant is chosen from the dropdown. When changed, the first variant’s ISBN is shown and does not update until the page is refreshed. I have also attached the code. We really need to sort this out as quickly as possible. Please help on this.
{% assign default_variant = product.variants.first %}
<!-- Display the barcode/ISBN -->
{% if default_variant.barcode != blank %}
<div id="product-barcode">
ISBN: {{ default_variant.barcode }}
</div>
{% endif %}
<script>
document.addEventListener("DOMContentLoaded", function() {
var selectElement = document.querySelector("[name='id']");
var barcodeElement = document.getElementById("product-barcode");
if (selectElement && barcodeElement) {
var variants = {{ product.variants | json }};
selectElement.addEventListener("change", function(event) {
var selectedVariantId = event.target.value;
var selectedVariant = variants.find(function(variant) {
return variant.id == selectedVariantId;
});
if (selectedVariant) {
if (selectedVariant.barcode) {
barcodeElement.innerHTML = "ISBN: " + selectedVariant.barcode;
barcodeElement.style.display = "block";
} else {
barcodeElement.style.display = "none";
}
}
});
}
});
</script>
To me, that selectElement looks strange, try with the following selector instead
var selectElement = document.querySelector('.variant-picker__form select');
if all your variants are in a select element.
In general, for debugging, you can use console.log to confirm that variables are ok and continue, or see if they return an unexpected value. Same for events, if they fire or not.
Nothing is wrong with the selector, however, the “change” event is not fired on this element, because this element is not changed by the visitor, only by Javascript code.
The input event is an UI-Event, it’s meant to fire when the user does action the page, not when scripts do.
What you can do?
use a MutationObserver to monitor this element, or
listen for event fired by theme code, like these:
This event passes a combination of selected options, not a variant id, so you can either fetch the value of selectElement (easier), or check the option values against your array.
Code should be like:
...
document.documentElement.addEventListener( "variant:selected", (e)=> {
let selectedVariantId = selectElement.value;
let selectedVariant = variants.find...
})
Should work ok on product page. If you have quick add modals, then the code would be more complex as you’d need to see what product generated the event.
if my post is helpful, please like it ♡ and mark as a solution -- this will help others find it
I found that in your current code you are using the Javascript. It’s a client side language and it’s update the data on Page Reload. If you want that ISBN update realtime then you need to use ajax.
AJAX works realtime and without Page Refresh.
Here is the solution on ajax based.
{% comment %}
Dynamic ISBN/Barcode Display for Product Variants
This code handles real-time ISBN updates when variants are selected
{% endcomment %}
{% assign current_variant = product.selected_or_first_available_variant %}
<!-- Display the barcode/ISBN -->
<div id="product-barcode" {% unless current_variant.barcode != blank %}style="display: none;"{% endunless %}>
<span class="barcode-label">ISBN: </span>
<span id="barcode-value">{{ current_variant.barcode }}</span>
</div>
<script>
(function() {
'use strict';
// Store all variants data
const variants = {{ product.variants | json }};
const barcodeContainer = document.getElementById('product-barcode');
const barcodeValue = document.getElementById('barcode-value');
if (!barcodeContainer || !barcodeValue) {
console.warn('Barcode elements not found');
return;
}
// Function to update ISBN display
function updateISBN(variantId) {
const selectedVariant = variants.find(variant => variant.id == variantId);
if (selectedVariant) {
if (selectedVariant.barcode && selectedVariant.barcode.trim() !== '') {
barcodeValue.textContent = selectedVariant.barcode;
barcodeContainer.style.display = 'block';
// Add loading state for better UX
barcodeContainer.classList.remove('loading');
} else {
barcodeContainer.style.display = 'none';
}
}
}
// Function to get current selected variant ID
function getCurrentVariantId() {
// Try multiple common selectors for variant selection
const selectors = [
'select[name="id"]',
'input[name="id"]:checked',
'[data-variant-id]',
'.product-form select',
'.variant-selector select'
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element) {
if (element.type === 'radio' && element.checked) {
return element.value;
} else if (element.tagName === 'SELECT') {
return element.value;
} else if (element.dataset.variantId) {
return element.dataset.variantId;
}
}
}
// Fallback to URL params
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('variant') || {{ current_variant.id }};
}
// AJAX-based variant change handler
function handleVariantChange(variantId) {
if (!variantId) return;
// Add loading state
barcodeContainer.classList.add('loading');
// Fetch variant data via AJAX
fetch(`${window.location.pathname}?variant=${variantId}&view=json`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
// Update from AJAX response if available
if (data && data.variant && data.variant.barcode !== undefined) {
if (data.variant.barcode && data.variant.barcode.trim() !== '') {
barcodeValue.textContent = data.variant.barcode;
barcodeContainer.style.display = 'block';
} else {
barcodeContainer.style.display = 'none';
}
} else {
// Fallback to local variant data
updateISBN(variantId);
}
})
.catch(error => {
console.warn('AJAX request failed, using local data:', error);
// Fallback to local variant data
updateISBN(variantId);
})
.finally(() => {
barcodeContainer.classList.remove('loading');
});
}
// Event delegation for dynamic elements
function setupEventListeners() {
// Listen for changes on the document to catch dynamically added elements
document.addEventListener('change', function(event) {
const target = event.target;
// Check if the changed element is a variant selector
if (
(target.name === 'id' || target.classList.contains('variant-selector') ||
target.closest('.product-form') || target.dataset.variantSelector) &&
target.value
) {
handleVariantChange(target.value);
}
});
// Listen for radio button clicks
document.addEventListener('click', function(event) {
const target = event.target;
if (target.type === 'radio' && target.name === 'id') {
handleVariantChange(target.value);
}
});
// Listen for custom variant change events (common in themes)
document.addEventListener('variant:change', function(event) {
const variantId = event.detail?.variant?.id || event.detail?.id;
if (variantId) {
handleVariantChange(variantId);
}
});
// Listen for Shopify's built-in variant events
document.addEventListener('variantChange', function(event) {
const variantId = event.detail?.variant?.id;
if (variantId) {
handleVariantChange(variantId);
}
});
}
// Initialize when DOM is ready
function init() {
setupEventListeners();
// Set initial state
const currentVariantId = getCurrentVariantId();
if (currentVariantId) {
updateISBN(currentVariantId);
}
// Watch for URL changes (for themes that update URL on variant change)
let currentUrl = window.location.href;
setInterval(() => {
if (window.location.href !== currentUrl) {
currentUrl = window.location.href;
const newVariantId = getCurrentVariantId();
if (newVariantId) {
handleVariantChange(newVariantId);
}
}
}, 500);
}
// Initialize immediately if DOM is ready, otherwise wait
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
<style>
#product-barcode {
margin: 10px 0;
font-weight: 500;
}
#product-barcode.loading {
opacity: 0.6;
}
#product-barcode.loading::after {
content: " (updating...)";
font-size: 0.8em;
color: #666;
}
.barcode-label {
font-weight: 600;
}
</style>
If this was helpful Mark as Solution and Like it.
Best,
Daniel.