基于视口可见产品卡片更新横向导航圆圈颜色的JS实现
I get your frustration right now—those navigation dots only update when you click them, not when you swipe through the product cards. Since you’re already using scroll-snap for horizontal scrolling, we can fix this by listening to the scroll event on your .usp-container and updating the corresponding radio input’s checked state based on which card is visible in the viewport.
Here’s a straightforward implementation:
Step 1: Add the JavaScript
We’ll listen for scroll events, calculate which card is active, and sync the navigation dots. We’ll also add a throttle to avoid performance hits from too-frequent scroll events, and restrict this logic to mobile/tablet screens.
// Throttle function to limit how often the sync runs (prevents jank) function throttle(func, limit) { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } } } // Core function to sync dots with visible card function syncNavigationDots() { const container = document.querySelector('.usp-container'); const items = document.querySelectorAll('.usp-item'); const radios = document.querySelectorAll('.usp-radio'); // Only run on mobile/tablet (adjust breakpoint to match your design) if (window.innerWidth > 768) return; // Calculate which card is in view const scrollPos = container.scrollLeft; const itemWidth = items[0].offsetWidth; const activeCardIndex = Math.round(scrollPos / itemWidth); // Update the corresponding radio input if (radios[activeCardIndex]) { radios[activeCardIndex].checked = true; } } // Attach the throttled scroll listener const scrollContainer = document.querySelector('.usp-container'); scrollContainer.addEventListener('scroll', throttle(syncNavigationDots, 100)); // Sync dots on page load too window.addEventListener('load', syncNavigationDots);
Step 2: How it works
- Throttle: Limits the sync function to run every 100ms, so it doesn’t fire nonstop during scrolling (keeps things smooth).
- Active card calculation: Since each
.usp-itemtakes 100% of the container width, dividing the scroll position by the item width gives us the index of the visible card. - Mobile/tablet restriction: The function only runs on screens narrower than 768px—tweak this number to match your breakpoint for tablet/mobile.
- Initial sync: Makes sure the correct dot is highlighted when the page first loads.
Step 3: No CSS changes needed
Your existing CSS already handles styling the active dot perfectly, so you can leave this part as-is:
.usp-radio:checked + label { background-color: #707070; }
Quick notes
- Double-check that your
.usp-itemelements are exactly 100% width of the container (your current CSS already enforces this withwidth: 100%;). - If your mobile/tablet breakpoint is different, adjust the
window.innerWidth > 768value to match your design specs. - The
scroll-snap-type: x mandatoryensures only one card is fully visible at a time, soMath.round()will always pick the correct active card index.
With this setup, the navigation dots will update automatically whether users click the dots or swipe through the cards.
内容的提问来源于stack exchange,提问作者Sybrentjuh

