需求描述
需要实现类似doc中文档大纲的效果,点击对应章节的名称时定位到相应的正文;而当正文滚动时,高亮显示对应的章节名称
实现思路
其实笔者一开始想到的是利用a标签页内跳转(也就是“锚点”),类似于Element-UI官方文档:单选框组
当鼠标悬浮到页内某一章节名称时,会显示符号为“¶”的锚点,点击锚点会在网页链接后拼接对应hash值实现页内跳转
但仅靠锚点难以实现滚动页面内容时,定位到对应章节名称。。还是得靠度娘
经过了一番搜索与尝试,我发现所有标注了“vue锚点双向滚动”的文章实际都采用了监听滚动/滚动页面的实现思路和“锚点”其实并无关联。。,总之,笔者最终翻到了这篇锚点双向定位博客,虽然原文的示例代码稍显粗糙,但这篇的本质其实就是经过个人润色的那篇博客哦☆♦☆!
示例代码
请在你本地引入了Element-UI的项目中,新建一个test.vue来运行以下示例代码~
<!--* @Author: smm* @Date: 2022-09-19 15:33:32* @LastEditors: smm* @LastEditTime: 2022-09-19 17:27:08* @Description: 章节联动滚动示例
-->
<template><div class="sectionTest"><div><el-radio-groupv-model="sectionIdx"@change="change"><el-radiov-for="(item, index) in sectionList":key="item":label="index">{{ item }}</el-radio></el-radio-group></div><divclass="contentBlock"@scroll="scrollEvent"><divv-for="item in sectionList":key="item"class="content">{{ `正文:${item}` }}</div></div></div>
</template><script>
export default {name: ``,data () {return {sectionList: [`第一章`, `第二章`, `第三章`, `第四章`, `第五章`], // 章节标题列表sectionIdx: 0 // 当前的章节下标}},methods: {change () {let contents = document.querySelectorAll('.content')contents[this.sectionIdx].scrollIntoView({ block: 'start', behavior: 'smooth' })},scrollEvent (e) {const { sectionList } = thislet contents = document.querySelectorAll('.content')let nowTop = e.target.scrollTop // 滚动条目前的滚动距离let firstTop = contents[0].offsetTop // 第一个章节的顶部位置let idx = nulltry { // forEach循环只能通过抛出异常的方式终止sectionList.forEach((section, index) => {let contentTop = contents[index].offsetTop // 第index个章节的顶部位置let scrollDistance = contentTop - firstTop // 从第一个章节到第index个章节的滚动距离if (nowTop <= scrollDistance) {// 滚动条已滚动的距离少于第index个章节需要的滚动距离,说明当前正文处于第(index-1)的章节idx = Math.max(0, index - 1)throw new Error(`find section:${this.sectionIdx}`)}})} catch (e) { }// 滚动到最后一章(该章节高度超过正文容器)时,滚动条的滚动距离超过所有章节需要的滚动距离this.sectionIdx = idx === null ? sectionList.length - 1 : idxif (e.srcElement.scrollTop + e.srcElement.offsetHeight === e.srcElement.scrollHeight) {// 滚动条触底,处于最后一章(消除该章节高度低于正文容器时始终定位在倒数第二章的情况)this.sectionIdx = sectionList.length - 1}}}
}
</script><style lang="scss">
.sectionTest {height: 500px;background: #fff;padding: 0 20px;.contentBlock{height: 400px;overflow-y: auto;&::-webkit-scrollbar {width: 10px;height: 10px;}&::-webkit-scrollbar-thumb {background:#ccc;border: 1px solid green;border-radius: 5px;}&::-webkit-scrollbar-track {background: #666;border-radius: 5px;}}.content {border: 1px solid red;margin: 10px 0;color: orangered;&:nth-child(even) {height: 500px;background: #aaa;}&:nth-child(odd) {height: 300px;background: #eee;}}
}
</style>
参考网址
[1] 锚点双向定位
[2] Element-UI单选框组
[3] Js中forEach跳出本次循环和终止循环