vue3 锚点定位 点击滚动高亮

功能描述

点击导航跳到对应模块的起始位置,并且高亮点击的导航;
滚动到相应的模块时,对应的导航也自动高亮;

效果展示

在这里插入图片描述

注意事项

  1. 一定要明确哪个是要滚动的盒子;
  2. 滚动的高度要减去导航栏的高度;
  3. 当前在导航1,点击导航4时,会出现导航2、导航3和导航4依次高亮的现象,定义变量判断当前为点击时不监听滚动事件即可;
    isScroll.value = false

核心代码

onMounted(() => {
  nextTick(() => {
  	// 监听滚动的盒子的滚动事件
    document.getElementById('scrollBox').addEventListener("scroll", onScroll);
  })
})

onBeforeUnmount(() => {
  document.getElementById('scrollBox').removeEventListener('scroll', onScroll)
})


const scrollToAnchor = (id, index) => {
 activeIndex.value = index
  var anchor = document.getElementById(id);
  var scrollBox = document.getElementById('scrollBox')
  nextTick(() => {
    scrollBox.scrollTo({
      top: anchor.offsetTop - 30,
      behavior: 'smooth'
    });
 })
  isScroll.value = false
   // 点击时暂停页面的滚动监听事件,防止点击时导航高亮出现走马灯效果;
  // 此处的定时器存在缺陷,点击完2秒之内滚动还是会偶现走马灯现象;
  let timeId = '';
  clearTimeout(timeId);
  timeId = setTimeout(() => {
    isScroll.value = true;
  }, 2000);
};


// 滚动监听器
const onScroll = ()=> {
  if(!isScroll.value) return
  // 获取所有锚点元素
  const navContents = document.querySelectorAll('.container .section')
  // 所有锚点元素的 offsetTop
  const offsetTopArr = []
  navContents.forEach(item => {
    offsetTopArr.push(item.offsetTop-30)
  })
  // 获取当前文档流的 scrollTop
  const scrollTop = document.getElementById('scrollBox').scrollTop
  // 定义当前点亮的导航下标
  let navIndex = 1
  for (let n = 1; n <= offsetTopArr.length; n++) {
    // 如果 scrollTop 大于等于第 n 个元素的 offsetTop 则说明 n-1 的内容已经完全不可见
    // 那么此时导航索引就应该是 n 了
    if (scrollTop >= offsetTopArr[n-1]) {
      navIndex = n
    }
  }
  // 把下标赋值给 vue 的 data
  activeIndex.value = navIndex
}

完整代码

<template>
  <div class="box" id="scrollBox" v-loading="loading">
    <div class="fixed-box">
      <div v-for="(i) in defaultTabs" :class="{active_anchor: activeIndex === i}">
          <a @click="scrollToAnchor(`section${i}`, i)">
            <span v-if="i == 1">导航1</span>
            <span v-if="i == 2">导航2</span>
            <span v-if="i == 3">导航3</span>
            <span v-if="i == 4">导航4</span>
            <span v-if="i == 5">导航5</span>
          </a>
        </div>
    </div>
    <div class="container" id="printcontent">
      <div class="section" id="section1">
        <h3>日历</h3>
        <el-calendar v-model="value" />
      </div>
      <div class="section" id="section2">
        <h3>带边框列表</h3>
        <el-descriptions class="margin-top" title="" :column="4" border>
          <template v-for="i in 4">
            <el-descriptions-item label="Username">kooriookami</el-descriptions-item>
            <el-descriptions-item label="Telephone">18100000000</el-descriptions-item>
            <el-descriptions-item label="Place" :span="2">Suzhou</el-descriptions-item>
            <el-descriptions-item label="Remarks">123123</el-descriptions-item>
            <el-descriptions-item label="Username">一一一</el-descriptions-item>
            <el-descriptions-item label="Remarks">123123</el-descriptions-item>
            <el-descriptions-item label="Remarks">-</el-descriptions-item>
            <el-descriptions-item label="Remarks">-</el-descriptions-item>
            <el-descriptions-item label="Remarks">-</el-descriptions-item>
          </template>
        </el-descriptions>
      </div>
      <div class="section" id="section3">
        <h3>普通表格</h3>
        <el-table :data="tableData" style="width: 100%">
          <el-table-column prop="date" label="Date" width="180" />
          <el-table-column prop="name" label="Name" width="180" />
          <el-table-column prop="address" label="Address" />
        </el-table>
      </div>
      <div class="section" id="section4">
        <h3>普通卡片</h3>
        <el-card class="box-card">
          <template #header>
            <div class="card-header">
              <span>Card name</span>
              <el-button class="button" text>Operation button</el-button>
            </div>
          </template>
          <div v-for="o in 10" :key="o" class="text item">{{ 'List item ' + o }}</div>
          <template #footer>Footer content</template>
        </el-card>
      </div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref, nextTick, onBeforeUnmount } from 'vue'
const loading = ref(false)
const value = ref(new Date())
const defaultTabs = ref([1, 2, 3, 4, 5])
const activeIndex = ref(1)
const isScroll = ref(true) // 点击导航栏时,暂时停止监听页面滚动
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
]

onMounted(() => {
  nextTick(() => {
    document.getElementById('scrollBox').addEventListener("scroll", onScroll);
  })
})

onBeforeUnmount(() => {
  document.getElementById('scrollBox').removeEventListener('scroll', onScroll)
})

const scrollToAnchor = (id, index) => {
 activeIndex.value = index
  var anchor = document.getElementById(id);
  var scrollBox = document.getElementById('scrollBox')
  nextTick(() => {
    scrollBox.scrollTo({
      top: anchor.offsetTop - 30,
      behavior: 'smooth'
    });
 })
  // 点击时暂停页面的滚动监听事件,防止点击时导航高亮出现走马灯效果;
  // 此处的定时器存在缺陷,点击完2秒之内滚动还是会偶现走马灯现象;
  isScroll.value = false
  let timeId = '';
  clearTimeout(timeId);
  timeId = setTimeout(() => {
    isScroll.value = true;
  }, 2000);
};

// 滚动监听器
const onScroll = ()=> {
  if(!isScroll.value) return
  // 获取所有锚点元素
  const navContents = document.querySelectorAll('.container .section')
  // 所有锚点元素的 offsetTop
  const offsetTopArr = []
  navContents.forEach(item => {
    offsetTopArr.push(item.offsetTop-30)
  })
  // 获取当前文档流的 scrollTop
  const scrollTop = document.getElementById('scrollBox').scrollTop
  // 定义当前点亮的导航下标
  let navIndex = 1
  for (let n = 1; n <= offsetTopArr.length; n++) {
    // 如果 scrollTop 大于等于第 n 个元素的 offsetTop 则说明 n-1 的内容已经完全不可见
    // 那么此时导航索引就应该是 n 了
    if (scrollTop >= offsetTopArr[n-1]) {
      navIndex = n
    }
  }
  // 把下标赋值给 vue 的 data
  activeIndex.value = navIndex
}

</script>
<style scoped lang="scss">
.box{
  // width: 98%;
  margin: auto;
  height: 100%;
  overflow: auto;
}
.fixed-box{
  width: calc(100% - 200px);
  background: white;
  position: fixed;
  top: 84px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  z-index: 99;
  padding: 5px 30px;
  box-sizing: border-box;
  box-shadow: 1px 1px 1px #ccc;
  span {
    padding: 0 10px;
    list-style: none;
  }
}
.container {
  padding-top: 15px;
  position: relative;
}
.section {
  margin: 20px 0;
}
:deep(.el-tabs ){
  --el-tabs-header-height: 50px;
}
.active_anchor {
  color: #409eff;
}
</style>


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/323346.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis相关命令详解及其原理

Redis概念 Redis&#xff0c;英文全称是remote dictionary service&#xff0c;也就是远程字典服务。这是kv存储数据库。Redis&#xff0c;包括所有的数据库&#xff0c;都是请求-回应模式&#xff0c;通俗来说就是数据库不会主动地要给前台推送数据&#xff0c;只有前台发送了…

【HarmonyOS4.0】第十篇-ArkUI布局容器组件(二)

三、层叠布局容器&#xff08;Stack&#xff09; 堆叠容器组件 Stack的布局方式是把子组件按照设置的对齐方式顺序依次堆叠&#xff0c;后一个子组件覆盖在前一个子组件上边。 注意&#xff1a;Stack 组件层叠式布局&#xff0c;尺寸较小的布局会有被遮挡的风险&#xff0c; …

JS常用插件 Swiper插件 实现轮播图

Swiper介绍 Swiper 是一款免费以及轻量级的移动设备触控滑块的js框架 中文官网地址: https://www.swiper.com.cn/ 点击查看Swiper演示&#xff0c;里面的功能和样式十分丰富&#xff0c;根据自己的需求选择 中文教程中详细介绍了如何使用Swiper API文档中介绍了各个模块以及参…

计算机导论05-计算机网络

文章目录 计算机网络基础计算机网络概述计算机网络的概念计算机网络的功能计算机网络的组成 计算机网络的发展计算机网络的类型 网络体系结构网络互联模型OSI/RM结构与功能TCP/IP结构模型TCP/IP与OSI/RM的比较 网络地址与分配IP地址构成子网的划分IPv6 传输介质与网络设备网络传…

电脑桌面便签在哪设置?备忘录软件哪个好?

好记性不如烂笔头&#xff01;相信很多打工族在电脑面前办公的时候&#xff0c;都需要随时记录工作中的事项&#xff0c;有的用TXT记录&#xff0c;有的手写笔记&#xff0c;还有一些用电脑桌面便签类软件。而当我们待办事项繁多的时候&#xff0c;手写或文本记录并不能有效帮我…

图解python | 字符串及操作

1.Python元组 Python的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。 元组使用小括号&#xff0c;列表使用方括号。 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可。 tup1 (ByteDance, ShowMeAI, 1997, 2022) tup2 (1…

老师假期有工资么

老师在享受假期的时候&#xff0c;很多人都会好奇&#xff1a;教师寒暑假会有工资吗&#xff1f; 老师也是劳动者&#xff0c;享有法律规定的各种权益。其中之一就是在规定的工作时间内获得报酬。既然老师在正常工作日上班有工资&#xff0c;那么在假期呢&#xff1f; 实际上…

数字化时代,VR全景展示如何让用户一窥全貌?

数字化时代&#xff0c;VR全景展示为各行各业提供了无限的可能性。随着VR全景技术的逐步普及&#xff0c;VR全景展示以其独特的呈现方式和新颖十足的交互体验&#xff0c;正在不断改变着人们对于展示宣传的理解。 传统的展示方式&#xff0c;通常需要将产品、图文、品牌等元素集…

【计算机组成与体系结构Ⅱ】指令调度与分支延迟(实验)

实验4&#xff1a;指令调度与分支延迟 一、实验目的 1. 加深对指令调度技术的理解。 2. 加深对分支延迟技术的理解。 3. 熟练采用指令调度技术解决流水线中的数据冲突的方法。 4. 进一步理解指令调度技术对CPU性能的改进。 5. 进一步理解延迟分支技术对CPU性能的改进。 二…

企业组网搭建有哪些?

在当今全球化的商业环境中&#xff0c;集团公司必须建立起一个无缝连接的网络&#xff0c;以确保高效的信息传输和资源共享。为实现这一目标&#xff0c;选择可靠而安全的网络组网方案至关重要。本文将介绍几种主要的集团公司网络组网方案&#xff0c;以帮助企业根据其具体需求…

人人都在用的PDF软件,也要接入ChatGPT了

随着人工智能技术的不断进步和发展&#xff0c;Chatbot技术的应用已经逐渐从娱乐和社交领域扩展到了更多的实际场景中。在办公软件领域&#xff0c;聊天机器人已经成为了提升工作效率、减少人力投入的重要工具&#xff0c;多家头部企业都在探讨将AI接入到软件及应用中的使用方案…

深入解析阻塞队列BlockingQueue及源码(超详细)

一、基础概念 1.1 BlockingQueue BlockingQueue 是 java.util.concurrent 包提供的用于解决并发生产者 - 消费者问题的最有用的类。 1.1.1 队列类型&#xff1a; 无限队列 &#xff08;unbounded queue &#xff09; - 几乎可以无限增长 有限队列 &#xff08; bounded qu…

python基础语法看一篇就够了,全网最全python语法笔记汇总

前言 Python 是一种代表简单思想的语言&#xff0c;其语法相对简单&#xff0c;很容易上手。不过&#xff0c;如果就此小视 Python 语法的精妙和深邃&#xff0c;那就大错特错了。 如能在实战中融会贯通、灵活使用&#xff0c;必将使代码更为精炼、高效&#xff0c;同时也会极…

Plane Geometry (Junior High School)

初中平面几何&#xff0c; ACBD, ∠CAD60&#xff0c;∠C40&#xff0c;求∠B Vertical Calculation-CSDN博客 Rectangular Area-CSDN博客

Rectangular Area

长方形ABCD&#xff0c; 点E 点F 在边AB上&#xff0c;点A&#xff0c; 点B&#xff0c; 点E&#xff0c; 点F不重合&#xff0c;连接DE&#xff0c;连接CF&#xff0c;相较于点O&#xff0c;已知S△EOF 4&#xff0c; S△COB 16&#xff0c;求S □ABCD &#xff1f; Plane …

YOLO (1) abaconda 安装 cuda、版本确定、pytorch 安装

一、简述 年底了&#xff0c;身上也没有其他项目&#xff0c;恰好乘着这个机会学一下pytorch 和YOLOv8. 1、下载abaconda https://repo.anaconda.com/archive/ 2、安装 环境变量要✔ 其他一直下一步 3、测试 (base) C:\Users\alber>conda -V conda 23.1.0(base) C…

算法通关村第十四关—数据流的中位数(黄金)

数据流中中位数的问题 LeetCode295,中位数是有序列表中间的数。如果列表长度是偶数&#xff0c;中位数则是中间两个数的平均值。 例如&#xff1a;[2,3,4]的中位数是3 [2,3]的中位数是(23)/22.5 实现 MedianFinder 类: MedianFinder() 初始化 MedianFinder 对象。void addNum(…

漏洞修复整理

一、Geoserver Apache HTTP/2拒绝服务漏洞&#xff08;CVE-2023-44487&#xff09;、Eclipse Jetty 资源管理错误漏洞(CVE-2023-26048)、Eclipse Jetty 信息泄露漏洞(CVE-2023-26049) 受影响版本&#xff1a;9.4.53以下版本 处理方式&#xff1a;原地升级 &#xff08; jdk版本…

开源ERP系统Odoo安装部署并结合内网穿透实现公网访问本地系统

文章目录 前言1. 下载安装Odoo&#xff1a;2. 实现公网访问Odoo本地系统&#xff1a;3. 固定域名访问Odoo本地系统 前言 Odoo是全球流行的开源企业管理套件&#xff0c;是一个一站式全功能ERP及电商平台。 开源性质&#xff1a;Odoo是一个开源的ERP软件&#xff0c;这意味着企…

ESP系列入门教程(二)——之简单驱动温湿度传感器DHT11【附 ESP32 / ESP8266 通用代码】

ESP系列入门教程<二> 概要技术名词简介● ESP系列简介 硬件连接实现代码实现●Demo&#xff1a;驱动DHT11温湿度传感器●编译注意事项&#xff1a;添加DHT库 概要 最近在跟着几个大佬的教学视频做项目。陆续会更新记录一些要点&#xff0c;便于后期总结笔记的时候进行引…