最近有项目需要使用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)事件触发:根据滑动位置的临界条件,来判断是否应该触发刷新和加载事件。

部分实现代码如下:

// scroller.js

export default class Scroller {
constructor(el, option) {
this._el = el
this._parent = el.parentNode
this._option = option
this._pos = 0
this._handleScrollStart = this.handleScrollStart.bind(this)
this._handleScroll = this.handleScroll.bind(this)
this.init()
}
init() {
this._el.addEventListener('touchstart', this._handleScrollStart)
this._el.addEventListener('touchend', this._handleScroll)
if(this._option.auto) {
this.handleRefresh()
}
}
destroy() {
this._el.removeEventListener('touchstart', this._handleScrollStart)
this._el.removeEventListener('touchend', this._handleScroll)
}
handleScrollStart(e) {
const touch = e.targetTouches[0] || e.changedTouches[0]
this._pos = touch.clientY
}
handleScroll(e) {
const touch = e.targetTouches[0] || e.changedTouches[0]
const delta = touch.clientY - this._pos
if(delta >= this._option.threhold) {
// 手势向下,且下行滚动距离超过判定阈值
if(this._parent.scrollTop == 0) {
this.handleRefresh(e)
}
} else if(this._parent.scrollHeight > this._parent.clientHeight && delta <= -this._option.threhold){
// 内容可滚动,且上行滚动距离超过判定阈值
if(this._parent.scrollTop > this._parent.scrollHeight - this._parent.clientHeight - this._option.threhold){
this.handleLoad(e)
}
}
}
handleRefresh(...params) {
this._option.onRefresh && this._option.onRefresh.apply(this, params)
}
handleLoad(...params) {
this._option.onRefresh && this._option.onLoad.apply(this, params)
}
}

调用demo:

var inner = document.getElementsById('inner')
var list = []
var pageIndex = 1
function getMockData() {
const newData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => i+(pageIndex-1)*10)
setTimeout(() => {
if(pageIndex==1) { list = newData } else if(pageIndex < 5) { list.push(...newData) } else { alert('没有更多内容了') }
pageIndex++
inner.innerHTML = list.map(i => `<div class="inner-item">${i}</div>`).join('')
}, 300)
}
function onRefresh() {
pageIndex = 1
getMockData()
}
new Scroller(inner, {
threhold: 20,
onRefresh: onRefresh,
onLoad: getMockData,
auto: true
})

大家可以自己试用一下,感觉效果还不错~~~欢迎留言区讨论交流。

作者:京东零售 陈震

来源:京东云开发者社区

缕析条分Scroll属性的更多相关文章

  1. 你不知道的CSS背景—css背景属性全解

    CSS背景在网页设计中使用频率非常高,然而对于这个开发人员很熟悉的CSS属性,却隐藏着许多不为初级开发人员熟知的细节,这篇文章尝试扒开这层不为人知的面纱. 首先列举一下CSS中关于元素背景的所有属性并 ...

  2. 深入理解滚动scroll

    前面的话 前面两篇博文分别介绍过偏移大小.客户区大小.本文介绍元素尺寸中内容最多的一部分——滚动scroll 滚动宽高 scrollHeight scrollHeight表示元素的总高度,包括由于溢出 ...

  3. js鼠标及对象坐标控制属性详细解析

    对js鼠标及对象坐标控制属性进行了详细的分析介绍.  offsetTop获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算顶端位置. offsetLeft获取对象相对于版面或由 ...

  4. js中offsetHeight、clientHeight、scrollHeight等相关属性区分总结

    今天再次遇到了offset***.client***.scroll***的这三类属性的问题,总是混淆,现归纳总结如下: 大体上来说可以这样理解: client***属性(clientWidth.cli ...

  5. jacascript 滚动 scroll 与回到顶部

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 滚动 scroll scrollHeight 表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分: ...

  6. 元素大小-偏移量(offset)客户区大小(client)滚动大小(scroll)

    一.偏移量---offset 1.定位父级 在理解偏移大小之前,首先要理解offsetParent.人们并没有把offsetParent翻译为偏移父级,而是翻译成定位父级,很大原因是offsetPar ...

  7. JavaScript---详解scroll

    scroll scroll--译为‘滚动’,他是非常常用的属性. 滚动宽高 scrollHeight scrollHeight表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分(不要误解为只有 ...

  8. 强制css属性生效

    今天在写一个隐藏滚动条的css时,设置的overflow-x: hidden; overflow-y: scroll;属性死活不生效, 在谷歌浏览器中查看,发现这两条属性是被划掉的.如图. 这表明别处 ...

  9. Web前端篇:CSS常用格式化排版、盒模型、浮动、定位、背景边框属性

    目录 Web前端篇:CSS常用格式化排版.盒模型.浮动.定位.背景边框属性 1.常用格式化排版 2.CSS盒模型 3.浮动 4.定位 5.背景属性和边框属性 6.网页中规范和错误问题 7.显示方式 W ...

  10. jacascript 滚动scroll

    滚动 scroll scrollHeight 表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分: scrollWidth 表示元素的总宽度,包括由于溢出而无法展示在网页的不可见部分: 没有滚 ...

随机推荐

  1. 实时分布式低延迟OLAP数据库Apache Pinot探索实操

    @ 目录 概述 定义 特性 何时使用 部署 Local安装 快速启动 手动设置集群 Docker安装 快速启动 手动启动集群 Docker Compose 实操 批导入数据 流式导入数据 概述 定义 ...

  2. express获取登录服务器的IP地址

    let ip = (req.headers['x-real-ip'] || req.connection.remoteAddress).slice(7);      

  3. P7603 [THUPC2021] 鬼街(减半警报器模板)

    P7603 [THUPC2021] 鬼街(减半警报器模板) 前言 这是一个由 lxl 大佬提出的神奇 trick,第一次省选集训的时候有点颓,听完了没写.刚好明天又要讲这个不如写篇题解. 还是,我太弱 ...

  4. C++ Primer 5th Edition, Chapter 2, Solutions

    Exercise 2.1 Questions What are the differences between int, long, long long, and short? Between an ...

  5. AI天后,在线飙歌,人工智能AI孙燕姿模型应用实践,复刻《遥远的歌》,原唱晴子(Python3.10)

    忽如一夜春风来,亚洲天后孙燕姿独特而柔美的音色再度响彻华语乐坛,只不过这一次,不是因为她出了新专辑,而是人工智能AI技术对于孙燕姿音色的完美复刻,以大江灌浪之势对华语歌坛诸多经典作品进行了翻唱,还原度 ...

  6. 通过Serverless私有化部署ChatGPT Web

    作为开发人员,想要自己部署一个渠道访问或是想随时访问但是奈何魔法有限,又或是海外服务器太贵,不想耗费这个钱,本文借助 Serverless 来搭建一下私有 ChatGPT 服务,Serverless ...

  7. 2021-02-06:假设字符串str长度为N,请问最长回文子串的长度是多少?

    福哥答案2021-02-06: 1.动态规划.无代码,见图.2.中心扩展法.无代码.3.Manacher算法.有代码,见图.1)理解回文半径数组.2)理解所有中心的回文最右边界R,和取得R时的中心点C ...

  8. 2021-07-10:请返回arr中,求子数组的累加和,是<=K的并且是最大的。返回这个最大的累加和。

    2021-07-10:请返回arr中,求子数组的累加和,是<=K的并且是最大的.返回这个最大的累加和. 福大大 答案2021-07-10: 时间紧.见代码. 时间复杂度:O(N*logN).空间 ...

  9. Django django-rest-framework-simplejwt

    Django(75)django-rest-framework-simplejwt「建议收藏」 发布于2022-09-16 11:56:13阅读 2440   大家好,又见面了,我是你们的朋友全栈君. ...

  10. uni-app 运行项目

    运行-运行到浏览器-选择浏览器 运行新项目:真机测试需要打开真机的开发者选项usb安装调试功能