vue3 实现页面锚点,左边控制右边内容区域滚动

在 Web 应用中,锚点导航是一种常见的 UI 交互模式,尤其适用于文档、目录式页面或单页应用(SPA)。本文将使用 Vue3 实现一个带有锚点功能的页面:左侧是 Tab 控制菜单,右侧是可滚动的内容区域,选中的内容会自动对齐到顶部。

功能需求

左侧 Tab 控制导航,点击后滚动到对应内容。

右侧内容区域可滚动,滚动时 Tab 自动高亮。

内容不足时,仍然可以滚动到顶部

关键点

onScroll 方法中,我们遍历 sectionRefs 计算当前滚动位置,并更新 activeIndex,保证 Tab 与内容联动。

添加 spacer 作为占位元素,确保即使最后一个 Section 也能正确滚动到顶部。

效果图

完整代码示例

xml 复制代码
<template>
  <div class="container">
    <!-- 左侧Tab导航 -->
    <div class="tabs">
      <div
        v-for="(section, index) in sections"
        :key="index"
        :class="['tab', { active: activeIndex === index }]"
        @click="scrollToSection(index)"
      >
        {{ section.name }}
      </div>
    </div>
    
    <!-- 右侧内容区域,可滚动 -->
    <div class="content" ref="contentRef" @scroll="onScroll">
      <div
        v-for="(section, index) in sections"
        :key="index"
        :ref="el => sectionRefs[index] = el"
        class="section"
      >
        <h2>{{ section.name }}</h2>
        <p>{{ section.content }}</p>
      </div>
      
      <!-- 占位符,确保最后一个Section也能滚动到顶部 -->
      <div class="spacer"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue';

// 定义章节数据,每个章节包含标题和内容
const sections = ref([
  { name: 'Section 1', content: 'This is section 1' },
  { name: 'Section 2', content: 'This is section 2' },
  { name: 'Section 3', content: 'This is section 3' },
  { name: 'Section 4', content: 'This is section 4' }
]);

// 存储各个section的DOM引用
const sectionRefs = ref([]);
// 右侧滚动区域的引用
const contentRef = ref(null);
// 记录当前选中的Tab索引
const activeIndex = ref(0);

// 处理点击Tab,滚动到对应的Section顶部
const scrollToSection = (index) => {
  if (sectionRefs.value[index]) {
    const contentTop = contentRef.value.offsetTop;
    const targetOffset = sectionRefs.value[index].offsetTop - contentTop;
    contentRef.value.scrollTo({ top: targetOffset, behavior: 'smooth' });
  }
};

// 监听滚动,动态更新当前选中的Tab
const onScroll = () => {
  const scrollTop = contentRef.value.scrollTop;
  let currentActive = 0;
  sectionRefs.value.forEach((el, index) => {
    if (el.offsetTop - contentRef.value.offsetTop <= scrollTop) {
      currentActive = index;
    }
  });
  activeIndex.value = currentActive;
};
</script>

<style scoped>
.container {
  display: flex;
  height: 100vh;
}

/* 左侧Tab栏样式 */
.tabs {
  width: 200px;
  border-right: 1px solid #ddd;
  padding: 10px;
}

.tab {
  padding: 10px;
  cursor: pointer;
}

.tab.active {
  font-weight: bold;
  color: blue;
}

/* 右侧内容区域样式 */
.content {
  flex: 1;
  overflow-y: auto;
  height: 100vh;
  padding: 20px;
  position: relative;
}

/* 章节样式 */
.section {
  height: 300px;
  padding: 20px;
  border-bottom: 1px solid #ddd;
}

/* 空间占位,保证最后一个Section能滚动到顶部 */
.spacer {
  height: 100vh;
}
</style>
相关推荐
用户0595401744620 小时前
LLM对话记忆测试踩坑实录:手工回归30分钟,自动化后2分钟发现3个隐藏Bug
前端·css
Ashley的成长之路21 小时前
前端性能优化实战手册·第2篇:资源加载策略全解
前端·性能优化·资源加载·http/3·性能优化实战·资源加载优化
浅水壁虎21 小时前
vue基础(第二章 )
前端·javascript·vue.js
界面开发小八哥21 小时前
界面控件DevExtreme v26.1新版亮点——支持Angular 22
前端·javascript·angular.js·devexpress·ui开发·devextreme
Getflare21 小时前
前端 + UI 设计 + AI:这不是三个工种,是一个新三角能力模型(附自检清单)
前端·人工智能·ui
oil欧哟21 小时前
我做了一个 Vibe Coding 术语学习站:VibeHub
前端·ai·agent·独立开发·vibe coding
审小匠OpenCPAi1 天前
银行流水核查怎么自动化?单边匹配、双向勾稽与图聚类异常检测的工程对比
java·前端·人工智能·python·审计
Revolution611 天前
一个公共表格组件,是怎么一步步失控的
前端·前端工程化
腻害兔1 天前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:CRM 客户关系模块深度解析——从线索到回款,一套完整的 B2B 销售闭环是怎么搭的?
java·前端·javascript·vue.js·产品经理·ai编程