media-gallery.js
change all
if (!customElements.get('media-gallery')) {
customElements.define(
'media-gallery',
class MediaGallery extends HTMLElement {
constructor() {
super();
this.elements = {
liveRegion: this.querySelector('[id^="GalleryStatus"]'),
viewer: this.querySelector('.product-single__gallery'),
thumbnails: this.querySelector('.product-single__thumbnails'),
};
this.mql = window.matchMedia('(min-width: 750px)');
if (!this.elements.thumbnails) return;
// Ensure debounce is defined globally or imported, otherwise this line might fail if debounce is missing
if (typeof debounce !== 'undefined') {
this.elements.viewer.addEventListener('slideChanged', debounce(this.onSlideChanged.bind(this), 500));
} else {
// Fallback if debounce is not defined
this.elements.viewer.addEventListener('slideChanged', this.onSlideChanged.bind(this));
}
this.elements.thumbnails.querySelectorAll('[data-target]').forEach((mediaToSwitch) => {
mediaToSwitch.addEventListener('click', this.setActiveMedia.bind(this, mediaToSwitch.dataset.target, false));
});
}
onSlideChanged(event) {
const thumbnail = this.elements.thumbnails.querySelector(
`[data-target="${event.detail.currentElement.dataset.mediaId}"]`
);
this.setActiveThumbnail(thumbnail);
}
setActiveMedia(mediaId, prepend) {
const activeMedia =
this.elements.viewer.querySelector(`[data-media-id="${mediaId}"]`) ||
this.elements.viewer.querySelector('[data-media-id]');
if (!activeMedia) {
return;
}
this.elements.viewer.querySelectorAll('[data-media-id]').forEach((element) => {
element.classList.remove('is-selected');
});
activeMedia?.classList?.add('is-selected');
if (activeMedia) {
const targetSlide = document.querySelector(`#${mediaId}`);
if (targetSlide) {
// --- FIX START: Check if Flickity is initialized ---
const $gallery = $('.product-single__gallery');
const flkty = $gallery.data('flickity');
// Only proceed if Flickity instance exists and has cells
if (flkty && flkty.cells) {
const index = flkty.cells.findIndex(cell => cell.element.id === mediaId);
if (index !== -1) {
flkty.select(index);
}
} else {
console.warn('Flickity is not ready yet. Skipping slide selection.');
}
// --- FIX END ---
}
}
this.preventStickyHeader();
window.setTimeout(() => {
if (!this.mql.matches || this.elements.thumbnails) {
activeMedia.parentElement.scrollTo({ left: activeMedia.offsetLeft });
}
const activeMediaRect = activeMedia.getBoundingClientRect();
// Don't scroll if the image is already in view
if (activeMediaRect.top > -0.5) return;
const top = activeMediaRect.top + window.scrollY;
window.scrollTo({ top: top, behavior: 'smooth' });
});
this.playActiveMedia(activeMedia);
if (!this.elements.thumbnails) return;
const activeThumbnail = this.elements.thumbnails.querySelector(`[data-target="${mediaId}"]`);
this.setActiveThumbnail(activeThumbnail);
this.announceLiveRegion(activeMedia, activeThumbnail.dataset.mediaPosition);
}
setActiveThumbnail(thumbnail) {
if (!this.elements.thumbnails || !thumbnail) return;
this.elements.thumbnails
.querySelectorAll('button')
.forEach((element) => element.removeAttribute('aria-current'));
// Optional: Add aria-current back if needed
// thumbnail.querySelector('button').setAttribute('aria-current', true);
}
announceLiveRegion(activeItem, position) {
const image = activeItem.querySelector('.product__modal-opener--image img');
if (!image) return;
image.onload = () => {
this.elements.liveRegion.setAttribute('aria-hidden', false);
this.elements.liveRegion.innerHTML = window.accessibilityStrings.imageAvailable.replace('[index]', position);
setTimeout(() => {
this.elements.liveRegion.setAttribute('aria-hidden', true);
}, 2000);
};
image.src = image.src;
}
playActiveMedia(activeItem) {
if (window.pauseAllMedia) window.pauseAllMedia();
const deferredMedia = activeItem.querySelector('.deferred-media');
if (deferredMedia) deferredMedia.loadContent(false);
}
preventStickyHeader() {
this.stickyHeader = this.stickyHeader || document.querySelector('sticky-header');
if (!this.stickyHeader) return;
this.stickyHeader.dispatchEvent(new Event('preventHeaderReveal'));
}
removeListSemantic() {
if (!this.elements.viewer.slider) return;
this.elements.viewer.slider.setAttribute('role', 'presentation');
this.elements.viewer.sliderItems.forEach((slide) => slide.setAttribute('role', 'presentation'));
}
}
);
}
main.js add
$(window).on('load', function() {
// Locate the script tag containing variant data
const scriptTag = $('script[data-selected-variant]').first();
if (scriptTag.length) {
try {
const jsonData = JSON.parse(scriptTag.text().trim());
const featuredMediaId = jsonData.featured_media?.id;
const sectionIdValue = $('input[name="section-id"]').val();
// Ensure we have both IDs before proceeding
if (featuredMediaId && sectionIdValue) {
const mediaId = `${sectionIdValue}-${featuredMediaId}`;
const thumbnail = $(`li[data-att="${mediaId}"]`);
if (thumbnail.length) {
console.log('Thumbnail found, waiting for Flickity...');
// --- FIX: Polling mechanism to wait for Flickity initialization ---
const checkFlickityInterval = setInterval(() => {
const $gallery = $('.product-single__gallery');
const flkty = $gallery.data('flickity');
// Check if Flickity is fully initialized and has cells
if (flkty && flkty.cells && flkty.cells.length > 0) {
clearInterval(checkFlickityInterval); // Stop checking
console.log('Flickity ready. Triggering click.');
thumbnail.click(); // Trigger the click safely
}
}, 100); // Check every 100ms
// Safety timeout: Stop checking after 5 seconds to prevent infinite loops
setTimeout(() => {
clearInterval(checkFlickityInterval);
}, 5000);
}
}
} catch (e) {
console.error('Error parsing variant JSON:', e);
}
}
});