缕析条分 Scroll 属性 | 京东云技术团队
最近有项目需要使用 js 原生开发滑动组件,频繁要用到 dom 元素的各种属性,其中以各种类型的 height 和 top 属性居多,名字相近,含义也很容易搞混。因此特地总结归纳了一下常用的知识点,在文末我们来挑战实现一个简易的移动端 Scroll 组件。
要理解 height 和 top,要从盒模型开始说起,首先我们来认识一下 css3 中定义的盒模型。dom 元素在页面上实际占据的面积可以由下面这张图来说明:
我们可以把它想象成一枚鸡蛋,从外到内依次是
橙色区域——外边距 margin:鸡蛋壳
黑色区域——边框 border:蛋壳膜
绿色区域——内边距 padding:蛋白
白色区域——内容 content:蛋黄
我们真正关心的部分是内容 content,也就是鸡蛋营养最丰富的部分。content 外面包裹了这么多层,我们在页面中才会感觉 dom 结构整体疏密有致,不会被密密麻麻的文字和图片所困扰。为了更好地描述盒模型,web 规范中还定义了一些其他接口来描述他们,也就是我们今天要聊到的主角们:
在上面这张图中,我们描述了具有嵌套关系的两层 dom 元素,注意这里的内容区和前述一张图中有所不同,这里的内容区也是一个独立的 dom 元素(即它也有自己的 margin、border、padding 和 content!,为了简化起见,子元素不再具体绘制出来),子元素由于整体高度很高,甚至超出了父元素的高度,在父元素中不能完全展示出来(超出的不可见部分用灰色表示),也就是两者构成了滚动关系。下面我们来尝试描述以下属性:
一、clientHeight
只读属性。clientHeight 实际上就是垂直滚动条的高度,一般情况下,垂直滚动条都是要紧贴上下 border 的,因此 clientHeight = 上下 padding + 内容 height。别忘了有个特殊情况,当存在水平方向滚动条时,还需要考虑水平滚动条挤占了垂直滚动条的一部分空间,即:clientHeight = 上下 padding + 内容 height - 水平滚动条高度。用鸡蛋来比喻的话,clientHeight 就是剥了壳的鸡蛋。
二、clientTop
只读属性。描述了顶部 border 的宽度。顶部 border 容易让我们联想到头发的厚度,下次在镜子前可以对自己说:你的 clientTop 变少了,不过你也变强了。
三、offsetHeight
只读属性。offsetHeight 和 clientHeight 比较类似,观察可以发现,比 clientHeight 多了 border 这一层:offsetHeight = clientHeight + 上下 border。现在我们把时光回退到给鸡蛋剥壳的前一刻,鸡蛋从水里捞出来洗干净呈现的样子——offsetHeight。
四、offsetTop
只读属性。它返回当前元素相对于其 offsetParent 元素的顶部内边距的距离。这段距离是不包含自身和父元素的 border 宽度的,实际上:offsetTop=自己的上 margin + 父元素的上 padding,相当于鸡蛋和鸡蛋盒之间挡板的厚度。
五、scrollHeight
只读属性。描述了元素内容高度的度量,包括由于溢出导致的视图中不可见内容。我们可以 hack 一下,把父元素中不可见的内容也展示出来,子元素在垂直方向可以分为两部分:外层的 margin 和内部的 offsetHeight:
对于具有滚动关系的父子元素,其 scrollHeight 具有不同的含义:
(1)对于子元素而言,由于子元素本身不包含溢出部分,其 scrollHeight 和 clientHeight 具有相同的值
(2)对于父元素而言,由于父元素的内容实际上被子元素“撑起来”了,因此其 scrollHeight 为把内容区域“展开”后的实际高度:父元素 scrollHeight = 子元素的 offsetHeight + 子元素上下 margin + 自己的上下 padding
六、scrollTop
读写属性,可以获取或设置一个元素的内容垂直滚动的像素数。这个是目前为止咱们遇到的第一个可以支持设置的属性。
(1)初始状态时,内容垂直方向未滚动,其值为 0
(2)当内容垂直方向滚动到底时,由于内容区实际高度为 scrollHeight,可显示高度为 clientHeight,多出来的部分就是此时的 scrollTop 值为 scrollHeight - clientHeight
因此可以得出结论:0 <= scrollTop <= scrollHeight - clientHeight
七、实战
学习了以上属性和信息,我们模仿京东小程序的 scroll-view 组件功能,来实现一个 H5 版滑动组件的常见功能:列表的下拉刷新和上拉加载。要实现这个功能,大概可以分为 3 个核心要点:
(1)可滚动:需要有一个不可动的外壳和可滑动的内容区。
(2)手势识别:通过移动端的 touch 属性,我们可以对比 touchend 和 touchstart 的手指位置,来简单进行手势识别。
(3)事件触发:根据滑动位置的临界条件,来判断是否应该触发刷新和加载事件。
部分实现代码如下:
调用 demo:
大家可以自己试用一下,感觉效果还不错~~~欢迎留言区讨论交流。
作者:京东零售 陈震
来源:京东云开发者社区
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/5e6067a2b964dc81f3fff49b5】。文章转载请联系作者。
评论