项目中需要做一个详情说明锚点定位功能,插件没有找到合适的,就打算手写一个。先看效果图。。。点击右边tab,左边的列表就会切换到对应的位置;鼠标在左边滚动或者拉动滚动条,右边的tab也会选中对应的位置。
思路
1、第一时间想到scrollIntoView,在实践的过程中发现怎么都行不通,首先就是元素中的子元素获取不到,在mounted生命周期方法中,getElementsByClassName获取父元素是可以打印出来的,但你获取长度的时候会发现子元素length为0。所以scrollIntoView指定子元素去滚动到其位置就pass掉了。
2、父元素scrollTop,随便设置了一个值之后发现是可以滚动的,于是就指定使用这个方法了。
实践
1、首先第一步就是要获取每个列表需要滚动多少高度,放到一个集合里,然后点击右边tab,滚动到对应index的高度。想要获取这个高度集合就少不了要获取每个列表元素的高度,第一个元素需要滚动高度为0,第二个元素滚动高度为第一个元素高度,第三个元素滚动高度为第一个元素高度加上第二个元素高度,以此类推。。。
2、因为列表数据listData是网络获取的,所以获取每个子元素高度我是在watch监听里面获取的,每次数据变化,滚动高度集合都会刷新。获取到滚动高度集合之后就可以做到点击右边tab,左边滚动到相应的位置了。
3、左边scrollView去联动右边tab,父元素设置可纵向滚动可以设置@scroll监听事件,根据event.srcElement.scrollTop获取当前已滚动的高度,然后根据此高度在滚动高度集合的for循环中匹配对应的index,然后设置选中。
难点
1、设置选中this.curSelectIndex = index 是放在tab点击事件里还是@scroll监听事件里?我开始是两边都设置,后来发现点击tab执行scrollTop之后@scroll监听事件也会执行,就把tab点击事件里的去掉了
2、获取滚动高度集合的时候,在做累加的过程中出现精度丢失,导致在@scroll监听事件中匹配index时有误差,最后去除小数部分解决。
3、最后几条已经到底了,再点击右边最后几条tab会出现不能选中,原因是点击tab调用的scrollTop方法之后,@scroll监听事件就会执行,这时候@scroll监听事件里的滚动高度是小于滚动高度集合里最后几条的,所以只能选中默认最接近的index。最后我在tab点击事件里做定时设置选中tab,用一个变量onScrollEventCanSpecifyIndex来管理@scroll事件里是否可以设置选中index。
以上就是全部内容,完整可运行代码如下:
完整代码:
可复制直接运行
<template>
<div class="box">
<div id="scrollView" class="left" @scroll="onScroll">
<p :id="'s' + index" v-for="(item,index) in listData" :key="index">{{index}}、{{item}}</p>
</div>
<div class="right">
<p :class="curSelectIndex == index ? 'select' : ''" v-for="(item,index) in listData" :key="index" @click="scroll(index)">{{index}}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
listData: [],//列表数据
networkListData: [
'傻人有傻福,傻逼没有——鲁迅',
'你越往人群中靠近一寸寻找存在感,你内心就多一份孤独与落寞。',
'千万不要放纵自己,给自己找借口。对自己严格一点儿,时间长了,自律便成为一种习惯,一种生活方式,你的人格和智慧也因此变得更加完美。',
'人生就那么短,不要为了变成别人喜欢的样子,委屈了自己。该开心时开心,想发泄就发泄。过好自己生活的第一件事,就是为自己而活。',
'如果你不快乐,那就出去走走,世界这么大。风景很美、机会很多、人生很短,不要蜷缩在一处阴影中。',
'如果我们质疑一个人扯谎,我们就应该伪装信任他,由于他会变得愈来愈神勇而有自信,并更斗胆地扯谎,最后会自己揭开自己的面具。',
'当你感觉人生没那么如意,当你对自己的表现没那么满意,当你对自己爱的人或自己感到失望,当你觉得自己再也坚持不下去的时候,请记得对自己说:没关系,我们都是这样长大的。',
'没有谁天生是属于谁的,任何人来到你身边愿意为你停下脚步,都是一件值得珍惜的事。这世上什么东西都有个保质期,没有比心存感激更好的保质方法,爱是用心,不是敷衍。',
'善良给对了人,会对你感恩;善良给错了人,会让你寒心。心软给对了人,会对你情深;心软给错了人,会让你痛心。宽容给对了人,会对你热忱;宽容给错了人,会让你窝心。',
'尽己力,听天命,无愧于心,不惑于情,顺势而为,随遇而安,知错就改,迷途知返,在喜欢自己的人身上用心,在不喜欢自己的人身上健忘,如此一生,甚好。',
'我们不应被往昔的痛苦耗尽生命的火花,更不能用眼泪换取异性廉价的同情和怜悯。救赎应该通过自我,而不是别人。',
'幸福是可以通过学习来获得的,尽管它不是我们的母语。',
'伤痛使你更坚强,眼泪使你更勇敢,心碎使你更明智。所以,感谢过去吧,它会带给我们一个更好的未来。',
'生命不是上帝用于捕捉你的错误的陷阱。你不会因为一个错误而成为不合格的人。生命是一场球赛,最好的球队也有丢分的记录,最差的球队也有辉煌的一天。我们的目标是让我们的人生经历更加的丰富。',
'失败,并不是说明你差,而是提醒你该努力了!',
'若非生活苦,谁愿弯腰做硕鼠。',
'心软穷半生 财发狠心人 不要怕得罪人,该狠的时候就狠 人不狠,站不稳 心不狠难立足,记住心软是病,情深致命..............',
'别总花时间给共享单车上锁,当你20岁所拥有 别人,30岁所拥有的东西,那你就知道什么是快乐',
'光阴似箭,岁月如梭。',
'三人行,必有我师焉!',
],//模拟网络数据
curSelectIndex: 0,// 当前选中索引
elementHeightList: [],// 每一个元素高度集合(包括margin、padding等)
onScrollEventCanSpecifyIndex: true,//onScroll事件内是否可以指定curSelectIndex
}
},
mounted() {
this.getListData();
},
watch: {
// 监听数据变化,改变元素高度集合数据
listData() {
this.$nextTick(() => {
this.elementHeightList = [];
this.listData.forEach((item, index) => {
var height = $('#s' + index).outerHeight();
// 去小数
this.elementHeightList.push(Math.floor(height));
});
});
}
},
methods: {
getListData() {
setTimeout(() => {
this.listData = this.networkListData;
}, 100);
},
// 滚动到指定index
scroll(toIndex) {
let scrollView = document.getElementById('scrollView');
let scrollHeight = 0;
this.elementHeightList.forEach((item, index) => {
if (index < toIndex) {
scrollHeight += item;
}
});
scrollView.scrollTop = scrollHeight;
// 点击tab移动的 select状态不能由onScroll监听中来改变
this.onScrollEventCanSpecifyIndex = false;
setTimeout(() => {
this.curSelectIndex = toIndex ;
this.onScrollEventCanSpecifyIndex = true;
}, 1);
},
// 滚动监听
onScroll(e){
// 已经滚动到距离顶部距离
let scrollTop = e.srcElement.scrollTop;
let indexTop = 0;
this.elementHeightList.forEach((item, index) => {
if (scrollTop >= indexTop) {
indexTop += item;
if(this.onScrollEventCanSpecifyIndex){
this.curSelectIndex = index ;
}
}
});
},
}
}
</script>
<style lang="less" scoped>
.box {
display: flex;
width: 800px;
height: 500px;
margin: 0 auto;
padding-top: 100px;
.left {
flex: 1;
height: 500px;
overflow-y: auto;
border: 1px solid #dfdfdf;
p {
font-size: 18px;
padding: 10px 15px;
}
}
.right {
width: 300px;
margin-left: 50px;
p {
width: 70px;
padding: 5px 15px;
cursor: pointer;
}
.select {
color: white;
background: red;
}
}
}
</style>
结束语
大家如果有碰到什么问题或者有更好的解决办法欢迎评论区留言。