概述
对过长的列表内容优化的方法之一是将看不见的项目隐藏,要实现这个需求必须先判断元素是否可见,判断的方式有多种,大致上可以分成通用的和方便的。
在分析这个问题之前,先得了解元素尺寸相关的属性,本文将从基础开始讲解。
尺寸相关属性
尺寸属性可以大致分成三类,分别是 offset、client 和 scroll,每一个元素都有这 3 种尺寸属性,每一种的意义都不一样。
offset
offset 多和 offsetParent
相关联,offsetParent
指的是离该元素最近的外层已定位元素(后文简称”定位元素“),若是不存在这样的外层元素,则指向 body。
offsetTop
指的是该元素与定位元素在垂直方向上的距离,是从定位元素的边框内部到该元素的边框外部,也就是说相当于定位元素的 padding-top
加上该元素的 margin-top
。
要注意的是这个属性是不会改变的,即使该元素被滚动到容器外,取值也不会有变化。
offsetLeft
和 offsetTop
类似,指的是该元素与定位元素在水平方向上的距离,是从定位元素的边框内部到该元素的边框外部,也就是说相当于定位元素的 padding-left
加上该元素的 margin-left
。
同样不会被改变。
offsetWidth
这个倒是和定位元素没什么关系,指的是该元素从 border-left
到 border-right
的距离,包括了 padding 和 content 的宽度。
offsetHeight
同理,这个指的是该元素从 border-top
到 border-bottom
的距离,包括了 padding 和 content 的高度。
##client
client 指的多是元素本身可见部分(border、padding 和 content),在左右边框宽度相等的前提下,clientLeft * 2 + clientWidth === 元素可见部分宽度
。
clientTop
指的是该元素 border-top
的高度。
clientLeft
指的是该元素 border-left
的宽度。
clientWidth
指的是该元素 padding 和 content 的宽度总和。
clientHeight
指的是该元素 padding 和 content 的高度总和。
scroll
scroll 指的多是和滚动相关的尺寸。
scrollTop
指的是该元素的上方滚动的距离,简单来说就是上方隐藏区域的高度,如果没有滚动或不存在滚动条,那么值就是0。
要注意的是,这个的取值是会随着滚动的距离发生变化的。
scrollLeft
指的是该元素的左方滚动的距离,简单来说就是左方隐藏区域的高度,如果没有滚动或不存在滚动条,那么值就是0。
同样会发生变化。
scrollWidth
指的是元素的真实宽度,也就是算上了左右两侧滚动区内容的宽度。
scrollHeight
指的是元素的真实高度,也就是算上了左右两侧滚动区内容的高度。
一图总结
判断可视区域
判断的方法有多种,首先是基于尺寸相关属性的判断方法。
由于 offsetTop
是不变的,所以无法直接通过它来取得元素当前的位置,判断元素有没有出上边缘可以通过 el.offsetTop - container.scrollTop
进行,因为 offsetTop
记录了元素与容器的初始相距距离,scrollTop
反映了容器上方滚动出去的高度,相减后结果大于 -el.clientHeight
时可以判断出元素还有一部分处在容器内。
下边缘也能通过同样的方式去判断,元素的相距距离减去容器的滚动高度,结果小于容器的 clientHeight
时那么说明元素一定在容器下边框的上方。
结合两个条件可以得到一个判断方式:
1 | const elOffsetTop = el.offsetTop |
这个方式比较通用,因为这些属性都是浏览器早期就支持的,但是要注意重排的问题,这里多次访问元素的位置属性,会造成多次重排。
第二种方法是使用现成的 IntersectionObserver
API,这个 API 是后续才加入浏览器中的,所以对于一些早期的浏览器可能兼容性不高,但论使用时的复杂程度,这个 API 要比前一个方法简单:
1 | const io = new IntersectionObserver(entries => { |