Key CSS features employed:
scroll-snap-type: x mandatory on the track and scroll-snap-align: start on cards ensures smooth paging.overflow-x: auto with -webkit-overflow-scrolling: touch for native mobile swipe.Before diving into the code, let's understand the "why":
This script calculates how many cards fit on screen, updates the track position, and handles responsive resize events.
document.addEventListener('DOMContentLoaded', () => const track = document.querySelector('.slider-track'); const prevBtn = document.querySelector('.prev-btn'); const nextBtn = document.querySelector('.next-btn'); const trackWrapper = document.querySelector('.slider-track-wrapper'); const dotsContainer = document.querySelector('.dots-container');let currentIndex = 0; let cardWidth = 0; let gap = 24; // 1.5rem in pixels (default) let cardsPerView = 0; let totalCards = 0; let maxIndex = 0;
// Function to get the current card dimensions dynamically function updateDimensions() responsive product slider html css codepen work
function updateSliderPosition() const offset = -currentIndex * (cardWidth + gap); track.style.transform =
translateX($offsetpx); updateButtonsState();function updateButtonsState() prevBtn.disabled = currentIndex === 0; nextBtn.disabled = currentIndex >= maxIndex; prevBtn.style.opacity = prevBtn.disabled ? '0.5' : '1'; nextBtn.style.opacity = nextBtn.disabled ? '0.5' : '1';
function updateDots() if (!dotsContainer) return; const dotCount = maxIndex + 1; dotsContainer.innerHTML = ''; for (let i = 0; i < dotCount; i++) const dot = document.createElement('button'); dot.classList.add('dot'); if (i === currentIndex) dot.classList.add('active'); dot.addEventListener('click', () => currentIndex = i; updateSliderPosition(); updateDots(); ); dotsContainer.appendChild(dot);
// Event listeners nextBtn.addEventListener('click', () => if (currentIndex < maxIndex) currentIndex++; updateSliderPosition(); updateDots(); ); Key CSS features employed:
prevBtn.addEventListener('click', () => if (currentIndex > 0) currentIndex--; updateSliderPosition(); updateDots(); );
// Responsive: recalc on window resize let resizeTimer; window.addEventListener('resize', () => clearTimeout(resizeTimer); resizeTimer = setTimeout(() => updateDimensions(); , 150); );
// Initial setup updateDimensions();
// Optional: Touch/swipe support for mobile let touchStartX = 0; trackWrapper.addEventListener('touchstart', (e) => touchStartX = e.touches[0].clientX; ); Scroll Snap : scroll-snap-type: x mandatory on the
trackWrapper.addEventListener('touchend', (e) => const touchEndX = e.changedTouches[0].clientX; const diff = touchEndX - touchStartX; if (Math.abs(diff) > 50) if (diff > 0 && currentIndex > 0) currentIndex--; else if (diff < 0 && currentIndex < maxIndex) currentIndex++; updateSliderPosition(); updateDots(); ); );
While this slider is
Key CSS features employed:
scroll-snap-type: x mandatory on the track and scroll-snap-align: start on cards ensures smooth paging.overflow-x: auto with -webkit-overflow-scrolling: touch for native mobile swipe.Before diving into the code, let's understand the "why":
This script calculates how many cards fit on screen, updates the track position, and handles responsive resize events.
document.addEventListener('DOMContentLoaded', () => const track = document.querySelector('.slider-track'); const prevBtn = document.querySelector('.prev-btn'); const nextBtn = document.querySelector('.next-btn'); const trackWrapper = document.querySelector('.slider-track-wrapper'); const dotsContainer = document.querySelector('.dots-container');let currentIndex = 0; let cardWidth = 0; let gap = 24; // 1.5rem in pixels (default) let cardsPerView = 0; let totalCards = 0; let maxIndex = 0;
// Function to get the current card dimensions dynamically function updateDimensions()
function updateSliderPosition() const offset = -currentIndex * (cardWidth + gap); track.style.transform =
translateX($offsetpx); updateButtonsState();function updateButtonsState() prevBtn.disabled = currentIndex === 0; nextBtn.disabled = currentIndex >= maxIndex; prevBtn.style.opacity = prevBtn.disabled ? '0.5' : '1'; nextBtn.style.opacity = nextBtn.disabled ? '0.5' : '1';
function updateDots() if (!dotsContainer) return; const dotCount = maxIndex + 1; dotsContainer.innerHTML = ''; for (let i = 0; i < dotCount; i++) const dot = document.createElement('button'); dot.classList.add('dot'); if (i === currentIndex) dot.classList.add('active'); dot.addEventListener('click', () => currentIndex = i; updateSliderPosition(); updateDots(); ); dotsContainer.appendChild(dot);
// Event listeners nextBtn.addEventListener('click', () => if (currentIndex < maxIndex) currentIndex++; updateSliderPosition(); updateDots(); );
prevBtn.addEventListener('click', () => if (currentIndex > 0) currentIndex--; updateSliderPosition(); updateDots(); );
// Responsive: recalc on window resize let resizeTimer; window.addEventListener('resize', () => clearTimeout(resizeTimer); resizeTimer = setTimeout(() => updateDimensions(); , 150); );
// Initial setup updateDimensions();
// Optional: Touch/swipe support for mobile let touchStartX = 0; trackWrapper.addEventListener('touchstart', (e) => touchStartX = e.touches[0].clientX; );
trackWrapper.addEventListener('touchend', (e) => const touchEndX = e.changedTouches[0].clientX; const diff = touchEndX - touchStartX; if (Math.abs(diff) > 50) if (diff > 0 && currentIndex > 0) currentIndex--; else if (diff < 0 && currentIndex < maxIndex) currentIndex++; updateSliderPosition(); updateDots(); ); );
While this slider is