仅使用原生JavaScript实现平滑滚动:替代jQuery方案及Safari兼容问题解决
Hey there! I’ve run into that exact Safari compatibility issue with scrollIntoView({ behavior: 'smooth' }) before—older Safari versions just don’t support it. Let’s replace your jQuery smooth scroll with a fully native solution that works across all modern browsers, including Safari.
Step 1: Set Up Anchor Click Listeners
First, we’ll listen for clicks on all anchor links starting with #, and override their default instant jump behavior:
document.addEventListener('DOMContentLoaded', function() { // Grab all anchor links pointing to in-page sections const anchorLinks = document.querySelectorAll('a[href^="#"]'); anchorLinks.forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); // Stop the default jump action const targetId = this.getAttribute('href'); // Handle empty # links (smooth scroll to top) if (targetId === '#') { smoothScrollTo(0, 1000); return; } const targetElement = document.querySelector(targetId); if (targetElement) { // Calculate the target position relative to the document top const targetOffset = targetElement.getBoundingClientRect().top + window.pageYOffset; smoothScrollTo(targetOffset, 1000); } }); }); });
Step 2: Custom Smooth Scroll Function
Instead of relying on the inconsistent scrollIntoView smooth behavior, we’ll build our own using requestAnimationFrame—this works everywhere, including Safari. It even mimics jQuery’s default swing animation curve for a familiar, natural feel:
function smoothScrollTo(targetPos, duration) { const startPos = window.pageYOffset; const distance = targetPos - startPos; let startTime = null; function animateScroll(currentTime) { if (startTime === null) startTime = currentTime; const timeElapsed = currentTime - startTime; const progress = Math.min(timeElapsed / duration, 1); // Cubic easing to create smooth acceleration/deceleration const ease = easeInOutCubic(progress); window.scrollTo(0, startPos + (distance * ease)); // Continue animating until we reach the desired duration if (timeElapsed < duration) { requestAnimationFrame(animateScroll); } } // Easing function that matches jQuery's default swing animation function easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; } requestAnimationFrame(animateScroll); }
Why This Works for Safari
- We’re ditching the
scrollIntoView({ behavior: 'smooth' })API that Safari struggles with, usingrequestAnimationFrameinstead—this is supported in all modern browsers, even older Safari versions. - The custom easing function ensures the scroll feels just like your original jQuery code, not a stiff linear scroll.
- We handle edge cases like empty
#links, which smoothly scroll back to the top of the page.
Bonus: Fix Fixed Navbar Overlap
If you have a fixed navbar that covers your target sections when scrolling, adjust the target offset by subtracting the navbar’s height:
// Replace 'navbar' with your actual navbar ID or selector const navbar = document.getElementById('navbar'); const targetOffset = targetElement.getBoundingClientRect().top + window.pageYOffset - navbar.offsetHeight;
This ensures your target section is fully visible below the navbar when scrolling completes.
内容的提问来源于stack exchange,提问作者jac

