You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用原生JavaScript实现导航栏点击平滑滚动效果

纯JS实现导航点击平滑滚动方案

原有代码问题排查

  • 点击事件逻辑未闭合,缺少滚动执行、激活态样式添加的核心逻辑,代码截断直接导致功能失效
  • a标签硬编码内联onclick事件,和后续绑定的监听逻辑重复,href="#"默认跳顶部的行为容易和事件监听冲突
  • 直接调用原生滚动API时未计算固定导航栏的高度偏移,滚动后区块顶部被导航栏遮挡,会造成导航栏"消失"的视觉错觉
  • 未控制JS执行时机,若脚本在DOM渲染前加载,会因为获取不到元素直接报错,功能完全不生效

完整实现代码

function navBar() {
  const sections = document.getElementsByTagName('section');
  const navBarUL = document.getElementById("navBarULID");
  // 获取导航栏元素,用于后续高度计算
  const navEle = document.querySelector('header');
  let navHeight = navEle.offsetHeight;

  // 生成导航项,移除冗余内联事件
  for (let i = 0; i < sections.length; i++) {
    const newLI = document.createElement("li");
    // 用data属性存储对应section的ID,比硬编码索引更稳定
    const link = `<a href="javascript:void(0)" data-target="section${i+1}" id='link_no${i+1}'> Section ${i+1} </a>`;
    newLI.innerHTML = link;
    navBarUL.appendChild(newLI);
  }

  /**
   * 自定义平滑滚动函数,不依赖CSS原生平滑特性、不依赖scrollIntoView原生平滑参数
   * @param {HTMLElement} targetElement 滚动目标元素
   */
  function smoothScrollTo(targetElement) {
    // 目标滚动位置 = 元素距文档顶部距离 - 导航栏高度,预留导航显示空间
    const targetPosition = targetElement.offsetTop - navHeight;
    const startPosition = window.pageYOffset;
    const distance = targetPosition - startPosition;
    // 滚动总时长,单位ms,可自行调整滚动速度
    const duration = 600;
    let startTime = null;

    // easeInOutQuad缓动函数,实现自然的加减速滚动效果
    function ease(t, b, c, d) {
      t /= d / 2;
      if (t < 1) return c / 2 * t * t + b;
      t--;
      return -c / 2 * (t * (t - 2) - 1) + b;
    }

    // 逐帧执行滚动动画
    function animation(currentTime) {
      if (startTime === null) startTime = currentTime;
      const timeElapsed = currentTime - startTime;
      const nextScrollY = ease(timeElapsed, startPosition, distance, duration);
      window.scrollTo(0, nextScrollY);
      if (timeElapsed < duration) requestAnimationFrame(animation);
    }

    requestAnimationFrame(animation);
  }

  // 绑定导航项点击事件
  for (let i = 0; i < sections.length; i++) {
    const currentLink = document.getElementById('link_no' + (i + 1));
    currentLink.addEventListener('click', function(e) {
      e.preventDefault();
      // 实时更新导航高度,适配导航栏滚动时缩放的场景
      navHeight = navEle.offsetHeight;

      // 清除所有导航项的激活样式,倒序遍历避免动态集合索引错乱
      const allActiveLinks = document.getElementsByClassName('active-link');
      for (let j = allActiveLinks.length - 1; j >= 0; j--) {
        allActiveLinks[j].classList.remove('active-link');
      }
      // 为当前点击项添加激活样式
      this.classList.add('active-link');

      // 获取对应目标区块
      const targetId = this.getAttribute('data-target');
      const targetSection = document.getElementById(targetId);
      
      // 清除所有区块的激活样式
      for (let k = 0; k < sections.length; k++) {
        sections[k].classList.remove('your-active-class');
      }
      // 为当前目标区块添加激活样式
      targetSection.classList.add('your-active-class');

      // 执行自定义平滑滚动
      smoothScrollTo(targetSection);
    })
  }
}

// DOM完全加载完成后再初始化导航,避免获取元素失败
document.addEventListener('DOMContentLoaded', navBar);

关键逻辑说明

  • 滚动偏移计算:每次点击时实时获取导航栏高度,滚动位置减去导航高度,彻底避免区块内容被导航遮挡、导航视觉上消失的问题
  • 纯JS动画实现:基于requestAnimationFrame做逐帧滚动,搭配缓动函数实现自然的平滑效果,不需要依赖CSS scroll-behavior: smooth属性,也不需要调用scrollIntoView的平滑滚动参数
  • 遍历优化:删除激活样式时采用倒序遍历,适配getElementsByClassName返回动态HTMLCollection的特性,避免正序删除导致的索引错乱、样式清除不彻底问题
  • 稳定性优化:用data-target属性存储对应区块ID,代替硬编码索引,后续增减区块时不需要调整索引逻辑;href使用javascript:void(0)彻底阻断默认锚点跳转行为
  • 执行时机控制:将导航初始化绑定在DOMContentLoaded事件上,确保DOM渲染完成后再执行元素获取逻辑,避免脚本报错导致功能失效

注意事项

  • 请确保CSS中没有给html、body标签设置scroll-behavior: smooth属性,完全通过JS实现滚动逻辑,符合需求要求
  • 可通过修改duration参数调整滚动速度,数值越大滚动动画时长越长
  • 若导航栏为静态定位(非fixed固定),可直接将navHeight设为0,不需要做偏移计算

内容的提问来源于stack exchange,提问作者srohome

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:54:32