功能描述
点击导航跳到对应模块的起始位置,并且高亮点击的导航;
滚动到相应的模块时,对应的导航也自动高亮;
效果展示
注意事项
- 一定要明确哪个是要滚动的盒子;
- 滚动的高度要减去导航栏的高度;
- 当前在导航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>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论