vue3如何實現(xiàn)錨點定位點擊滾動高亮
更新時間:2024年04月28日 08:54:04 作者:易de
這篇文章主要介紹了vue3如何實現(xiàn)錨點定位點擊滾動高亮問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
功能描述
點擊導航跳到對應模塊的起始位置,并且高亮點擊的導航;
滾動到相應的模塊時,對應的導航也自動高亮;
效果展示
注意事項
- 一定要明確哪個是要滾動的盒子;
- 滾動的高度要減去導航欄的高度;
- 當前在導航1,點擊導航4時,會出現(xiàn)導航2、導航3和導航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)聽事件,防止點擊時導航高亮出現(xiàn)走馬燈效果; // 此處的定時器存在缺陷,點擊完2秒之內滾動還是會偶現(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 // 定義當前點亮的導航下標 let navIndex = 1 for (let n = 1; n <= offsetTopArr.length; n++) { // 如果 scrollTop 大于等于第 n 個元素的 offsetTop 則說明 n-1 的內容已經(jī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">導航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) // 點擊導航欄時,暫時停止監(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)聽事件,防止點擊時導航高亮出現(xiàn)走馬燈效果; // 此處的定時器存在缺陷,點擊完2秒之內滾動還是會偶現(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 // 定義當前點亮的導航下標 let navIndex = 1 for (let n = 1; n <= offsetTopArr.length; n++) { // 如果 scrollTop 大于等于第 n 個元素的 offsetTop 則說明 n-1 的內容已經(jī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>
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
vue實現(xiàn)原理this.$message實例詳解
這篇文章主要介紹了vue實現(xiàn)原理this.$message實例詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-03-03vue封裝echarts組件,數(shù)據(jù)動態(tài)渲染方式
這篇文章主要介紹了vue封裝echarts組件,數(shù)據(jù)動態(tài)渲染方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12go-gin-vue3-elementPlus帶參手動上傳文件的案例代碼
這篇文章主要介紹了go-gin-vue3-elementPlus帶參手動上傳文件的案例代碼,本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11vscode使用Eslint+Prettier格式化代碼的詳細操作
這篇文章主要介紹了vscode使用Eslint+Prettier格式化代碼,本文通過圖文示例代碼相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08