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

NextJS跨页面跳转后如何实现锚点平滑滚动

NextJS跨页面锚点平滑滚动问题修复

现有代码的核心错误

  • onClick绑定逻辑错误导致定时器提前执行:你直接将setTimeout()的执行结果作为onClick的回调传入,而非传入包裹定时器的函数。这会导致组件渲染阶段setTimeout就会被立即注册,不需要用户点击就会在2秒后触发滚动逻辑,这就是页面未点击也会自动滚动到program区块的直接原因。
  • 服务端渲染阶段非法访问浏览器API:NextJS默认会在服务端完成组件首屏渲染,服务端运行环境不存在浏览器专属的document全局对象,提前触发的定时器在服务端执行时调用document相关方法,就会抛出document is not defined的引用错误。
  • 固定延时等待页面加载的逻辑不可靠:写死2秒延迟完全依赖主观判断的加载时长,网络较差时2秒内页面DOM可能还未渲染完成,目标区块不存在会导致滚动失效;网络较好时用户需要空等2秒才会触发滚动,交互体验很差。
  • 事件绑定冗余:外层导航项div和内部a标签重复绑定了点击逻辑,事件冒泡会导致回调重复触发。

正确实现方案

不需要依赖固定延时,利用NextJS路由事件配合原生锚点能力实现,逻辑更稳定:

1. 修改导航栏跳转逻辑

直接在跳转路径上拼接锚点参数,移除导航组件内的滚动、延时逻辑,避免提前执行问题:

<div className={styles.navItem}>
  <Link href="/" passHref>
    <a className={styles.navLinks} onClick={toggle}>
      Home
    </a>
  </Link>
</div>
<div className={styles.navItem}>
  <Link href="/#about" passHref>
    <a className={styles.navLinks} onClick={toggle}>
      About Us
    </a>
  </Link>
</div>
<div className={styles.navItem}>
  <Link href="/#program" passHref>
    <a className={styles.navLinks} onClick={toggle}>
      Program Overview
    </a>
  </Link>
</div>
<div className={styles.navItem}>
  <Link href="/leaderboard" passHref>
    <a className={styles.navLinks} onClick={toggle}>
      Leaderboard
    </a>
  </Link>
</div>

2. 首页监听路由事件触发滚动

将所有浏览器API相关逻辑放到客户端执行的生命周期中,避免服务端渲染报错:

如果你用的是Pages Router(pages/目录结构)

在首页文件pages/index.js中添加路由监听逻辑,路由跳转完成、DOM渲染结束后再触发滚动:

import { useRouter } from 'next/router'
import { useEffect } from 'react'

export default function Home() {
  const router = useRouter()

  useEffect(() => {
    const scrollToHash = (url) => {
      const hash = url.split('#')[1]
      if (!hash) return
      // 延时0等当前帧DOM渲染完成即可,不需要等2秒
      setTimeout(() => {
        const targetEl = document.getElementById(hash)
        targetEl?.scrollIntoView({ behavior: 'smooth', block: 'end' })
      }, 0)
    }

    // 首次进入页面带锚点时触发滚动
    if (router.asPath.includes('#')) {
      scrollToHash(router.asPath)
    }

    // 监听路由跳转完成事件
    router.events.on('routeChangeComplete', scrollToHash)
    return () => {
      router.events.off('routeChangeComplete', scrollToHash)
    }
  }, [router])

  return (
    <main>
      {/* 首页其他内容 */}
      <section id="about">About Us 区块内容</section>
      <section id="program">Program Overview 区块内容</section>
    </main>
  )
}

如果你用的是App Router(app/目录结构)

不需要额外写JS监听逻辑,只要给对应区块设置匹配的id属性,再在全局CSS中添加平滑滚动配置即可:

html {
  scroll-behavior: smooth;
}

注意:所有访问document、window的浏览器端API,都要放到useEffect、事件回调这类仅在客户端执行的代码块中,从根源上避免服务端渲染报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:18:13