(function () {
const wrap = document.querySelector('.snap-slider'); // the container above
if (!wrap) return;
const prev = document.querySelector('.snap-prev');
const next = document.querySelector('.snap-next');
const dotsC = document.querySelector('.snap-dots');
const slides = [...wrap.children];
// Build dots based on however many slides there are
slides.forEach((_, i) => {
const b = document.createElement('button');
b.className = 'dot' + (i === 0 ? ' is-active' : '');
b.setAttribute('role', 'tab');
b.setAttribute('aria-label', 'Go to slide ' + (i + 1));
b.addEventListener('click', () =>
wrap.scrollTo({ left: wrap.clientWidth * i, behavior: 'smooth' })
);
dotsC.appendChild(b);
});
function index() {
return Math.round(wrap.scrollLeft / wrap.clientWidth);
}
function updateDots() {
const i = index();
[...dotsC.children].forEach((d, n) => d.classList.toggle('is-active', n === i));
}
wrap.addEventListener('scroll', () => {
clearTimeout(wrap._t);
wrap._t = setTimeout(updateDots, 80);
});
prev.addEventListener('click', () =>
wrap.scrollTo({ left: wrap.clientWidth * (index() - 1), behavior: 'smooth' })
);
next.addEventListener('click', () =>
wrap.scrollTo({ left: wrap.clientWidth * (index() + 1), behavior: 'smooth' })
);
})();
document.addEventListener('DOMContentLoaded', () => {
const sliders = document.querySelectorAll('.snap-slider');
sliders.forEach((slider) => {
const items = Array.from(slider.children);
if (items.length {
const first = items[0];
const w = first.getBoundingClientRect().width;
const cs = getComputedStyle(slider);
// support both gap and column-gap depending on your CSS
const gap = parseFloat(cs.gap || cs.columnGap || 0) || 0;
return w + gap;
};
let index = 0;
const goTo = (i, smooth = true) => {
const len = items.length;
index = ((i % len) + len) % len; // wrap both directions
slider.scrollTo({ left: index * stepSize(), behavior: smooth ? 'smooth' : 'auto' });
};
// keep index synced after manual scroll/swipe
let syncTimer;
slider.addEventListener('scroll', () => {
clearTimeout(syncTimer);
syncTimer = setTimeout(() => {
index = Math.round(slider.scrollLeft / stepSize());
}, 80);
});
// autoplay
const INTERVAL = 4000; // ms
let autoId = null;
const start = () => {
stop();
autoId = setInterval(() => goTo(index + 1), INTERVAL);
};
const stop = () => {
if (autoId) { clearInterval(autoId); autoId = null; }
};
// pause on hover/touch/focus; resume after
slider.addEventListener('mouseenter', stop);
slider.addEventListener('mouseleave', start);
slider.addEventListener('touchstart', stop, { passive: true });
slider.addEventListener('focusin', stop);
slider.addEventListener('focusout', start);
// pause when tab/window is hidden
document.addEventListener('visibilitychange', () => {
document.hidden ? stop() : start();
});
// keep alignment after resize
window.addEventListener('resize', () => {
setTimeout(() => goTo(index, false), 60);
});
// kick things off
goTo(0, false);
start();
});
});