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

Vue实现锚点菜单当前浏览区段对应项高亮的方案咨询

Vue 实现滚动锚点自动高亮的两种方案

以下两种方案均优先贴合Vue生态特性,不需要额外引入重型第三方组件库:

方案1:原生轻量实现(无任何第三方依赖)

适合不想引入额外依赖的场景,仅用Vue内置API和浏览器原生能力实现:

  • 首先定义响应式变量存储当前激活的区段ID,以及滚动节流函数优化性能:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const activeSectionId = ref('')
let scrollHandler = null

// 简单节流函数,避免滚动事件高频触发
const throttle = (fn, delay = 100) => {
  let lastRunTime = 0
  return (...args) => {
    if (Date.now() - lastRunTime >= delay) {
      fn.apply(this, args)
      lastRunTime = Date.now()
    }
  }
}

onMounted(() => {
  // 给所有目标区段加统一类名page-section方便批量获取
  const sections = document.querySelectorAll('.page-section')
  // 获取吸顶头部高度,避免遮挡导致的区段偏移计算错误
  const headerHeight = document.querySelector('header').offsetHeight

  scrollHandler = throttle(() => {
    let currentActiveId = ''
    sections.forEach(section => {
      // 计算区段距离页面顶部的有效偏移(减掉头部高度)
      const sectionTop = section.offsetTop - headerHeight
      if (window.scrollY >= sectionTop) {
        currentActiveId = section.id
      }
    })
    activeSectionId.value = currentActiveId
  })

  // 初始化执行一次,适配页面刚加载、带hash直接进入的场景
  scrollHandler()
  window.addEventListener('scroll', scrollHandler)
})

onUnmounted(() => {
  window.removeEventListener('scroll', scrollHandler)
})

// 你的菜单配置
const menuList = [
  {id: 'sec1', name: '首页'},
  {id: 'sec2', name: '产品'},
  {id: 'sec3', name: '关于我们'}
]
</script>
  • 模板部分绑定动态高亮类即可:
<template>
  <!-- 吸顶头部锚点 -->
  <header class="fixed-header">
    <a
      v-for="item in menuList"
      :key="item.id"
      :href="`#${item.id}`"
      :class="{active: activeSectionId === item.id}"
    >
      {{item.name}}
    </a>
  </header>

  <!-- 页面区段 -->
  <section
    v-for="item in menuList"
    :key="item.id"
    :id="item.id"
    class="page-section"
  >
    <!-- 区段内容 -->
  </section>
</template>

<style>
.active {
  /* 你的高亮样式,比如变色、加下划线 */
  color: #165DFF;
  border-bottom: 2px solid #165DFF;
}
/* 如果需要平滑跳转可以加这个全局样式 */
html {
  scroll-behavior: smooth;
}
</style>

方案2:用VueUse简化实现(性能更优)

如果项目已经引入VueUse工具库,直接用封装好的useIntersectionObserver即可,不需要自己写滚动监听逻辑:

<script setup>
import { ref, onMounted } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'

const activeSectionId = ref('')
const sectionRefs = ref([])
const headerHeight = document.querySelector('header').offsetHeight

onMounted(() => {
  sectionRefs.value.forEach(section => {
    useIntersectionObserver(
      section,
      ([{ isIntersecting }]) => {
        // 区段进入视口时更新激活ID
        if (isIntersecting) activeSectionId.value = section.id
      },
      {
        // 根边距减掉头部高度,区段碰到头部底部就触发判定
        rootMargin: `-${headerHeight}px 0 0 0`,
        threshold: 0 // 只要有1px进入视口就触发,需要区段半屏进入再高亮可以改成0.5
      }
    )
  })
})
</script>

模板部分仅需要给区段绑定ref即可,锚点部分和方案1完全一致:

<section
  v-for="item in menuList"
  :key="item.id"
  :id="item.id"
  :ref="el => sectionRefs.push(el)"
>
</section>

以上两种方案都支持锚点点击跳转、滚动浏览两种场景下的高亮自动同步,适配移动端、PC端所有现代浏览器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 21:54:03