Shopify’s default themes will show all variant images on a product page. If you have a product with many variant combinations it will result in a very repetitive image gallery (if each variant has an individual image attached). A little custom code can tweak this and result in a much better user experience by only showing selected variant images, as well as the general product image not attached to a variant.
The following instructions will work for Shopify’s Debut theme.
1. Prepare templates/product.dynamic_thumbnails.liquid
with metadata on the variant images
Find the {% for media in product.media %}
loop and add the bold code
below to attach the image variant IDs as data
attributes for each gallery item. Here the .product-single__thumbnails-item
is extended, so take care that the closing HTML tag is correct. Later, a JavaScript mechanism will grab the attributes’ images to show and hide the pictures.
<div class="product-single__thumbnails-slider-track" data-slider-track>
{% endif %}
{%- assign variant_images = product.images | where: 'attached_to_variant?', true | map: 'src' -%}
{% for media in product.media %}
<li class="product-single__thumbnails-item product-single__thumbnails-item--{{ section.settings.media_size }} {% if enable_thumbnail_slides == true %} product-single__thumbnails-item-slide{% endif %} js"{% if enable_thumbnail_slides == true %} data-slider-slide-index="{{ forloop.index0 }}" data-slider-item{% endif %}>
<li class="product-single__thumbnails-item product-single__thumbnails-item--{{ section.settings.media_size }} {% if enable_thumbnail_slides == true %} product-single__thumbnails-item-slide{% endif %} js"{% if enable_thumbnail_slides == true %} data-slider-slide-index="{{ forloop.index0 }}" data-slider-item{% endif %}
{%- if variant_images contains media.src -%}
{%- assign variants = '' | split: '' -%}
{%- assign tmp_variants = product.images | where: 'src', media.src | map: 'variants' -%}
{%- for variant in tmp_variants -%}
{%- assign variants = variants | concat: variant -%}
{%- endfor -%}
data-variant-ids={{ variants | map: 'id' | uniq | join: ',' }}
{%- endif -%}>
<a href="{{ media.preview_image | img_url: product_image_zoom_size, scale: product_image_scale }}"
class="text-link product-single__thumbnail product-single__thumbnail--{{ section.id }}"
data-thumbnail-id="{{ section.id }}-{{ media.id }}"
2. Update assets/theme.js
to conditionally show and hide variant images based on the selected variant
Here we extend the Variants
object, thus the following code has to be added within the Variants.prototype = Object.assign({}, Variants.prototype, { ... })
block. Add the following function, e.g. below the _updateImages
function:
/**
* Update thumbnails visibility
*
* @param {object} variant - Currently selected variant
*/
_updateThumbnails: function(variant) {
const allThumbs = document.querySelectorAll('[data-variant-ids]');
const keepThumbs = [];
allThumbs.forEach(function(el) {
const variantIds = el.dataset.variantIds.split(',');
if (!variantIds.length) {
return;
}
if (variantIds.includes('' + variant.id)) {
keepThumbs.push(el);
}
});
if (!keepThumbs.length) {
return;
}
allThumbs.forEach(function(el) {
el.classList.add('hide');
});
keepThumbs.forEach(function(el) {
el.classList.remove('hide');
});
},
This will update the thumbnail visibility based on the variant
argument.
The _updateThumbnails
function has to be added in two places:
First, in the _onSelectChange event handler, which is called whenever the user changes a variant (changes are bold
).
/**
* Event handler for when a variant input changes.
*/
_onSelectChange: function() {
var variant = this._getVariantFromOptions();
this.container.dispatchEvent(
new CustomEvent('variantChange', {
detail: {
variant: variant
},
bubbles: true,
cancelable: true
})
);
if (!variant) {
return;
}
this._updateMasterSelect(variant);
this._updateImages(variant);
this._updateThumbnails(variant);
this._updatePrice(variant);
this._updateSKU(variant);
this.currentVariant = variant;
if (this.enableHistoryState) {
this._updateHistoryState(variant);
}
},
Second, in the slate.Variants = (function() { ... }))()
block, when the page is loaded to apply the correct initial state:
/**
* Variant constructor
*
* @param {object} options - Settings from `product.js`
*/
function Variants(options) {
this.container = options.container;
this.product = options.product;
this.originalSelectorId = options.originalSelectorId;
this.enableHistoryState = options.enableHistoryState;
this.singleOptions = this.container.querySelectorAll(
options.singleOptionSelector
);
this.currentVariant = this._getVariantFromOptions();
this.singleOptions.forEach(
function(option) {
option.addEventListener('change', this._onSelectChange.bind(this));
}.bind(this)
);
// Initialize thumbnails
this._updateThumbnails(this.currentVariant);
}
Now the selected variant images and general product images will be shown!