在不支持 scroll-behavior: smooth 的旧版浏览器中,可以通过 javascript 监听滚动事件并手动计算位置来实现平滑滚动。核心做法是获取目标元素的坐标,利用 requestanimationframe 在每一帧更新 window.scrolly 或 element.scrolltop,直到到达目标位置。这种方法能确保在 ie11 等环境中也能获得一致的视觉体验。

如何为页面锚点链接添加平滑滚动效果
要为页面上的所有内部锚点链接(如 <a href="#section"></a>)添加平滑滚动,需要拦截点击事件,阻止默认跳转,然后调用平滑滚动函数。具体做法是遍历所有 a 标签,检查其 href 是否以 # 开头且指向当前页面的 ID,如果是,则绑定 click 事件处理器。在处理器中,通过 document.getElementById 获取目标元素,计算其与视口顶部的距离差,启动动画循环。

function smoothScroll(targetElement) {
const startY = window.pageYOffset;
const targetY = targetElement.getBoundingClientRect().top + startY;
const distance = targetY - startY;
let startTime = null;
function step(currentTime) {
if (!startTime) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / 500, 1); // 500ms duration
const ease = progress < 1 ? progress * (2 - progress) : 1; // easeInOutQuad
window.scrollTo(0, startY + distance * ease);
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
smoothScroll(targetElement);
}
});
});上述代码定义了 smoothScroll 函数,使用 easeInOutQuad 缓动函数使滚动先加速后减速。主程序遍历所有锚点,点击时获取目标 DOM 节点并传入函数。预期结果是页面平滑滚动至指定 ID 元素处,无闪烁。
立即学习“前端免费学习笔记(深入)”;
如何实现自定义滚动容器的平滑滚动
对于非 window 的滚动容器(如 <div>),实现原理相同,但需操作 <code>scrollTop 属性而非 window.scrollY。具体做法是获取滚动容器的引用,计算其内部子元素相对于容器顶部的偏移量,然后在动画循环中更新 container.scrollTop。这适用于侧边栏、聊天窗口等局部滚动区域。

function smoothScrollContainer(container, targetElement) {
const startY = container.scrollTop;
const targetY = targetElement.offsetTop;
const distance = targetY - startY;
let startTime = null;
function step(currentTime) {
if (!startTime) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / 500, 1);
const ease = progress < 1 ? progress * (2 - progress) : 1;
container.scrollTop = startY + distance * ease;
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
// 假设有一个 id="sidebar" 的 div 和一个 id="item-5" 的子元素
const sidebar = document.getElementById('sidebar');
const item = document.getElementById('item-5');
if (sidebar && item) {
smoothScrollContainer(sidebar, item);
}该段代码展示了针对特定容器的平滑滚动实现。通过修改 container.scrollTop 而非全局滚动位置,实现了局部区域的平滑移动。预期结果是 #sidebar 容器内的内容平滑滚动至 #item-5 可见。
当目标元素不在可视区域内时如何处理
当目标元素位于滚动容器之外或初始不可见时,直接计算 offsetTop 可能因容器自身滚动状态而产生偏差。具体做法是在计算目标位置前,先确保滚动容器已滚动到足够位置,或者直接使用 getBoundingClientRect() 获取元素相对于视口的绝对位置,再结合容器当前的 scrollTop 进行相对计算。更稳健的方式是使用 element.scrollIntoView({ behavior: 'auto' }) 作为 fallback,但在纯 JS 实现中,我们通常依赖 offsetTop 累加,前提是容器结构未发生动态变化。


















