vue3如何實現(xiàn)錨點定位點擊滾動高亮
更新時間:2024年04月28日 08:54:04 作者:易de
這篇文章主要介紹了vue3如何實現(xiàn)錨點定位點擊滾動高亮問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
功能描述
點擊導(dǎo)航跳到對應(yīng)模塊的起始位置,并且高亮點擊的導(dǎo)航;
滾動到相應(yīng)的模塊時,對應(yīng)的導(dǎo)航也自動高亮;
效果展示

注意事項
- 一定要明確哪個是要滾動的盒子;
- 滾動的高度要減去導(dǎo)航欄的高度;
- 當前在導(dǎo)航1,點擊導(dǎo)航4時,會出現(xiàn)導(dǎo)航2、導(dǎo)航3和導(dǎo)航4依次高亮的現(xiàn)象,定義變量判斷當前為點擊時不監(jiān)聽滾動事件即可;
isScroll.value = false
核心代碼
onMounted(() => {
nextTick(() => {
// 監(jiān)聽滾動的盒子的滾動事件
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
// 點擊時暫停頁面的滾動監(jiān)聽事件,防止點擊時導(dǎo)航高亮出現(xiàn)走馬燈效果;
// 此處的定時器存在缺陷,點擊完2秒之內(nèi)滾動還是會偶現(xiàn)走馬燈現(xiàn)象;
let timeId = '';
clearTimeout(timeId);
timeId = setTimeout(() => {
isScroll.value = true;
}, 2000);
};
// 滾動監(jiān)聽器
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
// 定義當前點亮的導(dǎo)航下標
let navIndex = 1
for (let n = 1; n <= offsetTopArr.length; n++) {
// 如果 scrollTop 大于等于第 n 個元素的 offsetTop 則說明 n-1 的內(nèi)容已經(jīng)完全不可見
// 那么此時導(dǎo)航索引就應(yīng)該是 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">導(dǎo)航1</span>
<span v-if="i == 2">導(dǎo)航2</span>
<span v-if="i == 3">導(dǎo)航3</span>
<span v-if="i == 4">導(dǎo)航4</span>
<span v-if="i == 5">導(dǎo)航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) // 點擊導(dǎo)航欄時,暫時停止監(jiān)聽頁面滾動
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'
});
})
// 點擊時暫停頁面的滾動監(jiān)聽事件,防止點擊時導(dǎo)航高亮出現(xiàn)走馬燈效果;
// 此處的定時器存在缺陷,點擊完2秒之內(nèi)滾動還是會偶現(xiàn)走馬燈現(xiàn)象;
isScroll.value = false
let timeId = '';
clearTimeout(timeId);
timeId = setTimeout(() => {
isScroll.value = true;
}, 2000);
};
// 滾動監(jiān)聽器
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
// 定義當前點亮的導(dǎo)航下標
let navIndex = 1
for (let n = 1; n <= offsetTopArr.length; n++) {
// 如果 scrollTop 大于等于第 n 個元素的 offsetTop 則說明 n-1 的內(nèi)容已經(jīng)完全不可見
// 那么此時導(dǎo)航索引就應(yīng)該是 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>總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue實現(xiàn)原理this.$message實例詳解
這篇文章主要介紹了vue實現(xiàn)原理this.$message實例詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-03-03
vue封裝echarts組件,數(shù)據(jù)動態(tài)渲染方式
這篇文章主要介紹了vue封裝echarts組件,數(shù)據(jù)動態(tài)渲染方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
go-gin-vue3-elementPlus帶參手動上傳文件的案例代碼
這篇文章主要介紹了go-gin-vue3-elementPlus帶參手動上傳文件的案例代碼,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11
vscode使用Eslint+Prettier格式化代碼的詳細操作
這篇文章主要介紹了vscode使用Eslint+Prettier格式化代碼,本文通過圖文示例代碼相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08

