业务场景:
描述:
有一个列表,可以上下滚动,每个列表项可以点击展开详情,且每时刻至多只展开一个列表项;要求点击的列表项滚动到中间(顶部和底部不做要求)。
思路:给每个元素添加一个点击事件,回调找到父元素(触发滚动条的元素),然后根据target的位置,调整其scrollHeight,实现点击元素在中央
代码:
给点击元素添加点击事件
el.addEventLisiener('click',callback)
找到滚轮触发的元素(一般是点击元素的父元素),
计算点击元素中心到滚动条BOX上界的距离,以及滚动条BOX的高度
callback(e) {
if(e.target) {
//计算元素中心到滚动条BOX上界的距离
let elRect = e.target.getBoundingClientRect()
let parentRect = e.target.parentNode.getBoundingClientRect()
let top = elRect.top - parentRect.top + elRect.height/2
//计算滚动条Box的高度以及其当前上部隐藏的高度
let height = elRect.height
let scrollTop = e.target.parentNode.scrollTop
let scrollBoxHeight = parentRect.height
//移动
e.target.parentNode.scrollTo({
top:top+scrollHeight-scrollBoxHeight/2,
behavior: 'smooth'
})
}
}
解析:
要理解上面代码,得要先知道一些知识。
首先要了解一个元素在整个浏览器中的位置信息以及滚动条属性
一个元素在浏览器的位置信息主要包括自己的宽高(width,height),自己的上界和浏览器视口上界的距离(top),左边界和视口左边界的距离(left)
看看下面这张图
一个盒子产生滚动条的条件是设置overflow属性,当内部元素总高度大于自身内部高度时,就会产生滚动条,当滚轮滑动,内部元素会跟着移动,但是只能通过父元素这个口子透射出来
当滚轮滑动,向上蜷缩或者说上部隐藏的高度就叫做scrollTop。
理解了上面几个概念,再回来看看上面的需求和代码,
我们需要把元素中间放置到滚动窗口(产生滚动条的元素的‘窗口’)中间,
则需要计算当前的scrollTop,加上元素中心到滚动窗口上界的距离,和即为要将元素中心滚动到滚动窗口上界,scrollTop需要变为多少。换一种理解,计算元素中心到滚动窗口上界距离,这个值如果为正,代表在下方,那我们把元素‘往上推’,scrollTop加上这个正值,这个值如果为负,代表在上方,那我们把元素‘往下推’,scrollTop加上这个负值。别忘了我们的目标是放在中间,那么scollTop应该减去滚动窗口的一半,使得元素‘再’往下走半个窗口的距离。
top: top+scrollHeight-scrollBoxHeight/2
这行代码就是这么来的