Shopify themes, liquid, logos, and UX
Hi everyone, I am stuck! I have tried to add this code into my theme (using Craft) and it won’t properly work. I am unsure what to do next. I tried to use the code in both theme.liquid and theme-editor.js
Both are not taking the code and I am still unable to get the correct variant selected when on the correlating photo.
const selectVariantByClickingImage = { // Create variant images from productJson object _createVariantImage: function (product) { const variantImageObject = {}; product.variants.forEach((variant) => { if ( typeof variant.featured_image !== 'undefined' && variant.featured_image !== null ) { const variantImage = variant.featured_image.src .split('?')[0] .replace(/http(s)?:/, ''); variantImageObject[variantImage] = variantImageObject[variantImage] || {}; product.options.forEach((option, index) => { const optionValue = variant.options[index]; const optionKey = `option-${index}`; if ( typeof variantImageObject[variantImage][optionKey] === 'undefined' ) { variantImageObject[variantImage][optionKey] = optionValue; } else { const oldValue = variantImageObject[variantImage][optionKey]; if (oldValue !== null && oldValue !== optionValue) { variantImageObject[variantImage][optionKey] = null; } } }); } }); return variantImageObject; }, _updateVariant: function (event, id, product, variantImages) { const arrImage = event.target.src .split('?')[0] .replace(/http(s)?:/, '') .split('.'); const strExtention = arrImage.pop(); const strRemaining = arrImage.pop().replace(/_[a-zA-Z0-9@]+$/, ''); const strNewImage = `${arrImage.join('.')}.${strRemaining}.${strExtention}`; if (typeof variantImages[strNewImage] !== 'undefined') { product.variants.forEach((option, index) => { const optionValue = variantImages[strNewImage][`option-${index}`]; if (optionValue !== null && optionValue !== undefined) { const selects = document.querySelectorAll('#'+ id + ' [class*=single-option-selector]'); const options = selects[index].options; for (let option, n = 0; (option = options[n]); n += 1) { if (option.value === optionValue) { selects[index].selectedIndex = n; selects[index].dispatchEvent(new Event('change')); break; } } } }); } }, _selectVariant: function() { const productJson = document.querySelectorAll('[id^=ProductJson-'); if (productJson.length > 0) { productJson.forEach((product) => { const sectionId = product.id.replace("ProductJson-", "shopify-section-"); const thumbnails = document.querySelectorAll('#'+ sectionId + ' img[src*="/products/"]'); if (thumbnails.length > 1) { const productObject = JSON.parse(product.innerHTML); const variantImages = this._createVariantImage(productObject); // need to check variants > 1 if (productObject.variants.length > 1) { thumbnails.forEach((thumbnail) => { thumbnail.addEventListener('click', (e) => this._updateVariant(e, sectionId, productObject, variantImages), ); }); } } }); } }, }; if (document.readyState !== 'loading') { selectVariantByClickingImage._selectVariant(); } else { document.addEventListener( 'DOMContentLoaded', selectVariantByClickingImage._selectVariant(), ); }
Solved! Go to the solution
This is an accepted solution.
Hi @Vaseformyplace,
Seems like there are some errors in the code. Try implementing these fixes by following the steps below:
const selectVariantByClickingImage = {
// Create variant images from productJson object
_createVariantImage: function (product) {
const variantImageObject = {};
product.variants.forEach((variant) => {
if (typeof variant.featured_image !== 'undefined' && variant.featured_image !== null) {
const variantImage = variant.featured_image.src
.split('?')[0]
.replace(/http(s)?:/, '');
variantImageObject[variantImage] = variantImageObject[variantImage] || {};
product.options.forEach((option, index) => {
const optionValue = variant.options[index];
const optionKey = `option-${index}`;
if (typeof variantImageObject[variantImage][optionKey] === 'undefined') {
variantImageObject[variantImage][optionKey] = optionValue;
} else {
const oldValue = variantImageObject[variantImage][optionKey];
if (oldValue !== null && oldValue !== optionValue) {
variantImageObject[variantImage][optionKey] = optionValue;
}
}
});
}
});
return variantImageObject;
},
_updateVariant: function (event, id, product, variantImages) {
const arrImage = event.src
.split('?')[0]
.replace(/http(s)?:/, '')
.split('.');
const strExtention = arrImage.pop();
const strRemaining = arrImage.pop();
const strNewImage = `${arrImage.join('.')}.${strRemaining}.${strExtention}`;
if (typeof variantImages[strNewImage] !== 'undefined') {
product.variants.forEach((option, index) => {
const optionValue = variantImages[strNewImage][`option-${index}`];
if (optionValue !== null && optionValue !== undefined) {
const selects = document.querySelectorAll('#'+ id + ' [class*=select__select]');
const options = selects[index].options;
for (let option, n = 0; (option = options[n]); n += 1) {
if (option.value === optionValue) {
selects[index].selectedIndex = n;
selects[index].dispatchEvent(new Event('change'));
break;
}
}
}
});
}
},
_selectVariant: function() {
const productJson = document.querySelectorAll('[id^=ProductJson-');
if (productJson.length > 0) {
productJson.forEach((product) => {
const sectionId = product.id.replace("ProductJson-", "MainProduct-");
const thumbnails = document.querySelectorAll('#'+ sectionId + ' .thumbnail img');
if (thumbnails.length > 1) {
const productObject = JSON.parse(product.innerHTML);
const variantImages = this._createVariantImage(productObject);
var _this = this;
document.addEventListener('click', function(e) {
e = e || window.event;
var target = e.target;
if(target.classList.contains('thumbnail')){
_this._updateVariant(target.querySelector('img'), sectionId, productObject, variantImages)
}
}, false);
}
});
}
},
};
if (document.readyState !== 'loading') {
selectVariantByClickingImage._selectVariant();
} else {
document.addEventListener(
'DOMContentLoaded',
selectVariantByClickingImage._selectVariant(),
);
}
<script src="{{ 'theme-editor.js' | asset_url }}" defer="defer"></script>
{% unless product == empty %}
<script type="application/json" id="ProductJson-{{ section.id }}">
{{ product | json }}
</script>
{% endunless %}
This should integrate the required feature into your thumbnail layout. Let us know if this works for you. If you find this suggestion helpful, please consider liking us and accepting this as the solution. Happy building 🔨😀
Hi there, this is Layoutbase - Homepage & Blog Page Builder. Seems like we can provide a quick fix! Would you kindly provide the URL to your store?
This is an accepted solution.
Hi @Vaseformyplace,
Seems like there are some errors in the code. Try implementing these fixes by following the steps below:
const selectVariantByClickingImage = {
// Create variant images from productJson object
_createVariantImage: function (product) {
const variantImageObject = {};
product.variants.forEach((variant) => {
if (typeof variant.featured_image !== 'undefined' && variant.featured_image !== null) {
const variantImage = variant.featured_image.src
.split('?')[0]
.replace(/http(s)?:/, '');
variantImageObject[variantImage] = variantImageObject[variantImage] || {};
product.options.forEach((option, index) => {
const optionValue = variant.options[index];
const optionKey = `option-${index}`;
if (typeof variantImageObject[variantImage][optionKey] === 'undefined') {
variantImageObject[variantImage][optionKey] = optionValue;
} else {
const oldValue = variantImageObject[variantImage][optionKey];
if (oldValue !== null && oldValue !== optionValue) {
variantImageObject[variantImage][optionKey] = optionValue;
}
}
});
}
});
return variantImageObject;
},
_updateVariant: function (event, id, product, variantImages) {
const arrImage = event.src
.split('?')[0]
.replace(/http(s)?:/, '')
.split('.');
const strExtention = arrImage.pop();
const strRemaining = arrImage.pop();
const strNewImage = `${arrImage.join('.')}.${strRemaining}.${strExtention}`;
if (typeof variantImages[strNewImage] !== 'undefined') {
product.variants.forEach((option, index) => {
const optionValue = variantImages[strNewImage][`option-${index}`];
if (optionValue !== null && optionValue !== undefined) {
const selects = document.querySelectorAll('#'+ id + ' [class*=select__select]');
const options = selects[index].options;
for (let option, n = 0; (option = options[n]); n += 1) {
if (option.value === optionValue) {
selects[index].selectedIndex = n;
selects[index].dispatchEvent(new Event('change'));
break;
}
}
}
});
}
},
_selectVariant: function() {
const productJson = document.querySelectorAll('[id^=ProductJson-');
if (productJson.length > 0) {
productJson.forEach((product) => {
const sectionId = product.id.replace("ProductJson-", "MainProduct-");
const thumbnails = document.querySelectorAll('#'+ sectionId + ' .thumbnail img');
if (thumbnails.length > 1) {
const productObject = JSON.parse(product.innerHTML);
const variantImages = this._createVariantImage(productObject);
var _this = this;
document.addEventListener('click', function(e) {
e = e || window.event;
var target = e.target;
if(target.classList.contains('thumbnail')){
_this._updateVariant(target.querySelector('img'), sectionId, productObject, variantImages)
}
}, false);
}
});
}
},
};
if (document.readyState !== 'loading') {
selectVariantByClickingImage._selectVariant();
} else {
document.addEventListener(
'DOMContentLoaded',
selectVariantByClickingImage._selectVariant(),
);
}
<script src="{{ 'theme-editor.js' | asset_url }}" defer="defer"></script>
{% unless product == empty %}
<script type="application/json" id="ProductJson-{{ section.id }}">
{{ product | json }}
</script>
{% endunless %}
This should integrate the required feature into your thumbnail layout. Let us know if this works for you. If you find this suggestion helpful, please consider liking us and accepting this as the solution. Happy building 🔨😀
Hi @layoutbase !
I followed your instructions and I’m still stuck. I removed the old codes and added the ones you’ve instructed, is there something I’m doing wrong? Is there a different code I should use to make this user friendly with a mobile device?
Hi @Vaseformyplace,
Could you elaborate a little more about the specific problem you are encoutering? The code should be optimized for mobile as well.
I checked a few of the product pages just now, including this one: https://vaseformyplace.com/products/y-dshh-mini-creative-nordic-style-resin-flower-vase-decoration-h... The code seems to be work as intended, the dropdown change to the variant selected from the gallery:
Looking forward to your reply!
I'm having the same problem, when I use the code and instructions you provided nothing changes, it looks and behaves the exact same as before I followed your instructions. The expected behavior I'm looking for is outlined in this document: https://help.shopify.com/en/manual/online-store/themes/themes-by-shopify/vintage-themes/customizing-...
Hi @timeforhelp
May I know which theme and theme version you are using? The code provided is theme specific such that the function can integrate into a Shopify's 2.0 theme. A different theme might behave differently. If you want this function as a global feature, it will require more complex code development. If you want more assistance, you can send a request here and we can go from there.
Best regards,
Layoutbase
Hi,
I'm using Craft 5.0.0, followed your steps and it didn't worked for me.
Since i'm not used to this, maybe i pasted the code into the wrong places.
May you specify where do i have to paste it?
Thank You
The code works, variant option updates in dropdown list as per variant image selected or clicked BUT when click "Add to cart" button, in the cart you get either the first option variant from the dropdown list or the one that you manually select in the dropdown list.
So, the code just updates the variant option in dropdown list by variant image clicked.
How to update the code so that correct option is being added to the cart?
thanks
Hi @layoutbase,
I have used your code initially and got the issue with wrong variant being added to cart (see my previous post).
I have used ChatGPT to share a fix for my issue and I ended up in a modified version of the JavaScript _updateVariant function, which solves my add-to-cart issue.
Here is the modified _updateVariant function code:
_updateVariant: function (event, id, product, variantImages) {
const arrImage = event.src
.split('?')[0]
.replace(/http(s)?:/, '')
.split('.');
const strExtention = arrImage.pop();
const strRemaining = arrImage.pop();
const strNewImage = `${arrImage.join('.')}.${strRemaining}.${strExtention}`;
if (typeof variantImages[strNewImage] !== 'undefined') {
product.variants.forEach((variant) => {
const variantId = variant.id;
const variantOptions = variantImages[strNewImage];
let isMatch = true;
for (let i = 0; i < variant.options.length; i++) {
if (variant.options[i] !== variantOptions[`option-${i}`]) {
isMatch = false;
break;
}
}
if (isMatch) {
// Update the variant ID in the form
const variantInput = document.querySelector('#' + id + ' input[name="id"]');
if (variantInput) {
variantInput.value = variantId;
variantInput.dispatchEvent(new Event('change', { bubbles: true }));
}
// Additional: trigger change event on the select elements
const selects = document.querySelectorAll('#'+ id + ' .select__select');
selects.forEach((select, index) => {
select.selectedIndex = [...select.options].findIndex(
option => option.value === variantOptions[`option-${index}`]
);
select.dispatchEvent(new Event('change', { bubbles: true })); // Trigger change event
});
}
});
}
},
Now it works perfectly. Thank you!
I encountered the same issue, and implementing this modified version of the JavaScript _updateVariant The function did help me. Have you tried other approaches, or do you have suggestions on ensuring they work consistently? Your assistance is much appreciated!
I appreciate your help.
By investing 30 minutes of your time, you can unlock the potential for increased sales,...
By Jacqui Sep 11, 2024We appreciate the diverse ways you participate in and engage with the Shopify Communi...
By JasonH Sep 9, 2024Thanks to everyone who participated in our AMA with 2H Media: Marketing Your Shopify St...
By Jacqui Sep 6, 2024