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
相关产品推荐
相关产品推荐

