At DCX, we frequently take over client websites where performance optimization tools like WP Rocket are used, often with aggressive JavaScript delay settings to improve load times. While this can significantly boost PageSpeed scores, it can also silently break interactive components on the site. One such case recently involved a Swiper carousel for WooCommerce product thumbnails, broken only when JavaScript delay was active.
The Problem
A client’s product gallery carousel was completely broken when JavaScript delay was enabled in WP Rocket. Oddly enough, disabling optimization fixed it. Upon investigation, we found that:
- The Swiper instance for product thumbnails was initialized by Elementor’s
woocommerce/single.min.js
. - When this file was delayed or blocked, Swiper didn’t initialize, and running it manually in the console didn’t help, because crucial DOM mutations were never applied.
- Without
.swiper-wrapper
,.swiper-slide
, and navigation buttons in the DOM, Swiper had nothing to hook into.
Even worse: on the unoptimized version, where everything loaded correctly, blocking woocommerce/single.min.js
immediately broke the carousel, proving the file was essential.
The Challenge
Most developers would just exclude the required scripts from delay:
elementor-swiper.min.js
frontend.min.js
swiper.min.js
woocommerce/single.min.js
elementorModules
ElementorProFrontendConfig
But at DCX, we don’t settle for patchy workarounds. Excluding all these files would:
- Slow down page loads
- Reduce PageSpeed scores
- Introduce unnecessary dependencies on Elementor’s frontend system (elementorModules, etc.)
Our Solution: Rebuilding the Carousel from Scratch
We wrote a standalone JavaScript function that:
- Waits for both Swiper and relevant DOM elements to be ready.
- Dynamically builds the DOM structure needed by Swiper.
- Initializes the carousel with the correct configuration.
- Works regardless of whether Elementor or WP Rocket delay is active.
Here’s the code of our re-implementation, it needs to be put before </head> inside header.php
<script nowprocket>
(() => {
const waitFor = (conditionFn, maxAttempts = 100, interval = 10) => {
return new Promise((resolve, reject) => {
let attempts = 0;
const timer = setInterval(() => {
if (conditionFn()) {
clearInterval(timer);
resolve();
} else if (++attempts >= maxAttempts) {
clearInterval(timer);
reject('Timeout waiting for condition');
}
}, interval);
});
};
const createButton = (cls, icon) => {
const btn = document.createElement('div');
btn.className = `elementor-swiper-button ${cls}`;
btn.setAttribute('tabindex', '0');
btn.setAttribute('role', 'button');
btn.setAttribute('aria-label', cls.includes('next') ? 'Next slide' : 'Previous slide');
const i = document.createElement('i');
i.className = icon;
btn.appendChild(i);
const span = document.createElement('span');
span.className = 'elementor-screen-only';
span.textContent = cls.includes('next') ? 'Next' : 'Previous';
btn.appendChild(span);
return btn;
};
const init = async () => {
try {
await waitFor(() => typeof Swiper === 'function');
await waitFor(() => document.querySelector('.flex-control-thumbs'));
const thumbs = document.querySelector('.flex-control-thumbs');
const parent = thumbs.closest('.woocommerce-product-gallery');
if (!parent) return console.warn('Gallery wrapper not found');
const swiperWrapper = document.createElement('div');
swiperWrapper.className = 'swiper swiper-thumbs-horizontal';
thumbs.parentNode.insertBefore(swiperWrapper, thumbs);
swiperWrapper.appendChild(thumbs);
thumbs.classList.add('swiper-wrapper');
[...thumbs.children].forEach(el => el.classList.add('swiper-slide'));
const prevBtn = createButton('elementor-swiper-button-prev', 'axetor-icon-angle-left');
const nextBtn = createButton('elementor-swiper-button-next', 'axetor-icon-angle-right');
swiperWrapper.appendChild(prevBtn);
swiperWrapper.appendChild(nextBtn);
new Swiper('.swiper-thumbs-horizontal', {
slidesPerView: 'auto',
spaceBetween: 10,
navigation: {
prevEl: prevBtn,
nextEl: nextBtn
}
});
console.log('Swiper thumbs initialized!');
} catch (e) {
console.warn(e);
}
};
init();
})();
</script>
Whether your sliders break under optimization, or you want a blazing-fast WooCommerce store with 100% working JS features – DCX can help.
Let us optimize your WordPress site the right way: fast, functional, and future-proof.