vue项目中实现导航栏锚点效果
需求: vue项目PC端详情页内容过多,在右侧或左侧加一个导航栏,通过点击某一项,页面平滑滚动到具体的内容上。并把这个功能封装成组件。思路:封装成组件复用的话,首先快捷键的每一项数据需要父组件传入;插值表达式打印v-if里的数据,是true是true为什么不显示,后面typeof之后发现Boolean类型通过跳转回来后变成了String类型由于跳转筛选页的时候,将该值通过query传参的方式,传
需求:
vue项目PC端详情页内容过多,在右侧或左侧加一个导航栏,通过点击某一项,页面平滑滚动到具体的内容上。并把这个功能封装成组件。
思路:
- 封装成组件复用的话,首先快捷键的每一项数据需要父组件传入;在父组件定义一个数组作为右侧导航栏的数据;由于左侧区域的某一块内容没有数据时,其对应的导航项不显示;
- 要给每一块的内容最外层绑定ref,是为了获取该dom元素,通过点击导航栏某一项时,让页面可以滚动到对应的元素内容;
- 导航在抽屉里做的,给最外层绑定ref=“drawer”,可以获取到当前页面最外层的dom元素,根据最外层容器的dom元素来获取滚动高度;
- 组件要接收导航栏数据、接收当前是否是抽屉(默认为抽屉,传false为普通正常页面)、接收当前页面所有的dom元素、接收样式(显示在页面的左侧还是右侧)
- 统一封装滚动方法:将滚动方法放在定时器中,在获取到最新DOM元素时,添加该定时器;在组件销毁时,清除定时器;定时器中主要功能:当滚动上去的高度+可见区域的高度==全文高度,导航栏选中最后一项;当第一项的距离可视窗口顶部距离>0,导航栏选中第一项。
- 点击方法:点击导航栏某一项,内容区域滚动到可视区域的顶部,导航栏选中该项。
代码:
具体页面
组件:
<template>
<div>
<div class="leftKey" :style="{ left: left || 'auto', right: right || 'auto', opacity: opacity }">
<span v-for="item in titleKey" :key="item.id">
<a v-if="item.flag" @click="goAnchor(item.id)" :class="{checkKey: item.checked}">{{item.name}}</a>
</span>
</div>
</div>
</template>
<script>
export default {
data () {
return {
timer: null,
scrollTop: 0,
anchorFlag: false,
ids: ''
}
},
created () {
// 在组件调用之前将所有项设置未选中,将第一项设置默认选中
this.titleKey.forEach(item => {
item.checked = false
})
this.titleKey[0].checked = true
},
mounted () {
// console.log('7777', this.parentRef)
this.fun = () => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
// scrollTop 网页被卷去的高
// clientHeight 网页可见区域高
// scrollHeight 网页正文全文高
// this.parentRef.drawer.$refs.drawer.scrollTop 获取当前抽屉滚动的高度
// document.documentElement.scrollTop || document.body.scrollTop 获取当前页面滚动的高度
var scrollTop = this.drawerFLag ? this.parentRef.drawer.$refs.drawer.scrollTop : (document.documentElement.scrollTop || document.body.scrollTop)
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
var scrollHeight = this.drawerFLag ? this.parentRef.drawer.$refs.drawer.scrollHeight : (document.documentElement.scrollHeight || document.body.scrollHeight)
this.scrollTop = scrollTop
// 网页被卷去的高+网页可见的高===网页全文高,就是滚动到最底部了,默认选中最后一项
// 为什么不能使用forEach循环,因为最后一项可能是false不显示,这个时候如果只遍历将全部设置为false,而把最后一项设为true此时没有可选中的项
// 设置一个变量为true,倒着遍历,若该项存在且变量为true,则将这一项设置为true,将变量设置为false;剩下的for循环会将每一项都设为false
if (scrollTop + clientHeight === scrollHeight) {
let lastFlag = true
for (let i = this.titleKey.length - 1; i >= 0; i--) {
if (this.titleKey[i].flag && lastFlag) {
this.titleKey[i].checked = true
lastFlag = false
} else {
this.titleKey[i].checked = false
}
}
return
}
// 监听id对应DOM与屏幕顶部的距离,第一项距离大于0,就默认选中第一项
// getBoundingClientRect().top 元素的上边 距离 可视窗口顶部的距离
if (this.parentRef[this.titleKey[0].id].getBoundingClientRect().top > 0) {
this.titleKey.forEach(item => {
item.checked = false
})
this.titleKey[0].checked = true
}
}, 50)
}
// vue使用$once清除定时器的问题,以防其他页面滚动时触发该定时器导致报错
// 通过$once来监听生命周期beforeDestroy钩子,在组件销毁前清除定时器
this.$once('hook:beforeDestroy', () => {
window.removeEventListener('scroll', this.fun, true)
})
// 通过$nextTick获取最新更新的DOM元素
this.$nextTick(() => {
window.addEventListener(
'scroll',
this.fun,
true
)
})
},
methods: {
// 点击导航栏的某一项,左侧内容区域滚动到对应的位置
goAnchor (id) {
// 如果点击选中的这一项id在传过来的dom元素中
// scrollIntoView() 方法让当前的元素滚动到浏览器窗口的可视区域内
// 让选中id对应的DOM置顶,即DOM与可视屏幕顶部重合
if (this.parentRef[id]) {
this.parentRef[id].scrollIntoView({
// 平滑过渡
behavior: 'smooth',
// 上边框与视窗顶部平齐。默认值
block: 'start'
})
// 遍历让所有项为false,点击项为true
this.titleKey.forEach(item => {
if (item.id === id) {
item.checked = true
} else {
item.checked = false
}
})
}
},
},
props: {
// 父组件传入的数据属性名
titleKey: {
// 类型
type: Array,
// 默认值
default: () => []
},
// 父组件传来的值是否是抽屉
// 默认是true也就是抽屉,如果不是则传false
drawerFLag: {
type: Boolean,
default: true
},
// 父组件的ref
parentRef: {
type: Object,
default: () => {}
},
// 居左显示
left: {
type: String,
default: ''
},
// 居右显示
right: {
type: String,
default: ''
},
// 设置透明度
opacity: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/variables.scss";
@import "~@/styles/index.scss";
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
}
.checkKey {
color: #E8380D;
border-bottom: 1px solid #E8380D;
background-color: #E7370D;
color: #fff;
}
.leftKey {
position: fixed;
width: 127px;
background: #fff;
box-shadow:0px 3px 8px 1px rgba(12,12,12,0.06);
font-size: 14px;
color: #333;
background-color: rgba(255,252,252,1);
top: 115px;
z-index: 999;
a {
display: block;
padding: 16px 0px 16px 21px;
border-top: 1px solid rgba(255,228,222,1);
border-left: 1px solid rgba(255,228,222,1);
border-right: 1px solid rgba(255,228,222,1);
}
a:last-child {
border-bottom: 1px solid rgba(255,228,222,1);
}
img {
padding-right: 2px;
}
}
</style>
main.js
import Vue from 'vue'
import ElementUI from 'element-ui'
// 全局注册导航栏组件
import shortcutKey from '@/components/ ShortcutKey'
Vue.component('ymShortcutKey', shortcutKey)
Vue.config.productionTip = false
Vue.use(ElementUI)
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
参考文章:Vue中实现锚点
涉及知识:
-
vue使用
$once
清除定时器mounted () { const that = this // 设置定时器 const testSetinterval = setInterval(() => { setTimeout(() => { console.log('test clearInterval') }, 0) }, 2000) // 通过$once来监听生命周期beforeDestroy钩子,在组件销毁前清除定时器。 this.$once('hook:beforeDestroy', () => { clearInterval(testSetinterval) }) },
-
scrollTop
网页被卷去的高
clientHeight
网页可见区域高
scrollHeight
网页正文全文高参考:
搞清clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop
DOM元素位置,滚动位置和鼠标事件位置相关属性函数总结
获取页面滚动高度
document.documentElement.scrollTop || document.body.scrollTop; -
this.$nextTick()
this.$nextTick()这个方法作用是当数据被修改后使用这个方法会回调获取更新后的dom再render出来(调用render()函数,重新编译。将vue模版里的逻辑如v-if、v-for等这些内容是浏览器不能识别的,必须通过js去转换为html,才能够显示页面。)
-
ref
与$refs
使用ref绑定DOM元素,通过this.$refs获取绑定的该元素。这样可以减少获取dom节点的消耗<input type="text" ref="input1"/> this.$refs.input1.value ="22";
-
setInterval()
与setTimeout()
setInterval的特点:一直循环调用函数,不会自己停止;需要用window.clearInterval(setInt);这个函数去停止循环
setTimeout的特点:只调用一次
setTimeout(“showTime()”,5000); //延迟5秒刷新页面
业务场景:
setTimeout用于延迟执行某方法或功能
setInterval则一般用于刷新表单,对于一些表单的假实时指定时间刷新同步参考:
setInterval()与setTimeout()计时器
setTimeout和setInterval的区别
setInterval和setTimeout的区别
更多推荐
所有评论(0)