功能描述
点击导航跳到对应模块的起始位置,并且高亮点击的导航;
滚动到相应的模块时,对应的导航也自动高亮;
效果展示

注意事项
- 一定要明确哪个是要滚动的盒子;
- 滚动的高度要减去导航栏的高度;
- 当前在导航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>总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论