你想知道的HTML位置信息都在这里了

1,569 阅读13分钟

前言

在一次的波纹效果的实现中,因为用到了很多的HTML的位置信息,之前记得也就一两个,这次去翻阅MDN后发现,关于HTML的位置信息竟然后很多参数。于是觉得有必要将这些位置信息参数的含义做个总结,方便以后参考。

1、Element

Element在是最通用的基类,一个DOM结构中所有的对象都是继承于它。包括后面介绍的HTMLElement也是继承于它。所以待会Element的属性HtmlElement也会拥有。

1.1、clientHeight/clientWidth/clientLeft/clientTop

这四个参数标识的是元素的内部高度/宽度/左边border的宽度/上面border的宽度。其标识的含义如下图所示:

clientHeight是元素内部的高度,包含内边距也就是padding-top/padding-bottom,但是不包含水平滚动条、边框、和margin。

同理clientWidth也是如此。

clientLeft表示一个元素的左边框的宽度,有一些兼容性的问题可以详细参考Element.clientLeft

clientTop也是如此。

四个参数的具体取值我们以下面的demo来讲解。

我们使用超多的内容去填充div标签,并且设置了overflow属性让其出现水平滚动条或者垂直滚动条,但是这四个参数不为所动,验证了上面我们刚才说的事实。

1.2、scrollHeight/scrollLeft/scrollLeftMax/scrollTop/scrollTopMax/scrollWidth

这6个参数和滚动相关,在页面的设计中难免会出现滚动条,想要知道用户滚到到哪个位置就需要用到上面的某些参数来辅助你的判断。

scrollHeight标识元素的内容高度,注意和刚才的clientHeight加以区别。当元素不足以展示全部内容的时候使用overflow属性导致出现不可视的内容区域(也就是出现滚动条)的时候,这些不可视的内容也是包括在内的。在没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值clientHeight相同,它包含了元素的padding,但不包含margin。

scrollWidth也是类似。

scrollLeft可以读取或设置元素滚动条到元素左边的距离。

Tips

注意如果这个元素的内容排列方向(direction) 是rtl (right-to-left) ,那么滚动条会位于最右侧(内容开始处),并且scrollLeft值为0。此时,当你从右到左拖动滚动条时,scrollLeft会从0变为负数(这个特性在chrome浏览器中不存在)

scrollLeftMax返回一个Number表示一个元素横向滚动条可滚动的最大距离,兼容性比较差,属于新特性,谨慎使用。

scrollTop类似于scrollLeft。

scrollTopMax类似于scrollLeftMax。

我们使用下面demo来说明上面几个参数的用法。

在demo中我们输出上面的四个参数(scrollLeftMax/scrollTopMax当前Chrome(v61)不支持)。 结果是:

scrollHeight: 222 scrollLeft: 0 scrollTop: 0 scrollWidth: 336

当我们把内容减少让其不要出现滚动条的时候:

可以看到:

scrollHeight: 102 scrollLeft: 0 scrollTop: 0 scrollWidth: 102 clientHeight: 102 clientWidth: 102 clientLeft: 2 clientTop: 2

clientHeight === scrollHeight/clientWidth === scrollWidth

1.3、getBoundingClientRect()/getClientRects()

这两个方法也是用来获取元素的位置以及大小相关的信息。getClientRects返回的是个数组,数组中有很多个类似getBoundingClientRect返回的对象。getBoundingClientRect返回的永远是最外框框的那个矩形区域相关的坐标偏移对象;而getClientRects是多行文字区域的坐标偏移集合,在非IE浏览器下,只对inline的标签有反应。

一般getBoundingClientRect方法用的多一点。我们可以很容易获取某个元素的偏移值。甚至高宽都能很轻松的计算出来。

1.3.1、getBoundingClientRect()

getBoundingClientRect返回一个DOMRect对象

DOMRect对象包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。

对象里面的元素如下表(引自DOMRect)

属性 类型 描述
bottom float Y 轴,相对于视口原点(viewport origin)矩形盒子的底部。只读。
height float 矩形盒子的高度(等同于 bottom 减 top)。只读。
left float X 轴,相对于视口原点(viewport origin)矩形盒子的左侧。只读。
right float X 轴,相对于视口原点(viewport origin)矩形盒子的右侧。只读。
top float Y 轴,相对于视口原点(viewport origin)矩形盒子的顶部。只读。
width float 矩形盒子的宽度(等同于 right 减 left)。只读。
x float X轴横坐标,矩形盒子左边相对于视口原点(viewport origin)的距离。只读。
y float Y轴纵坐标,矩形盒子顶部相对于视口原点(viewport origin)的距离。只读。

在这个demo中我们看到对应的值:

(bottom,height,left,right,top,width,x,y): 114,106,9,115,8,106,9,8}

如何得到这些值呢?我绘制了这么一张图片,各个参数的取值由来罗列出来了:

需要注意的有以下几点:

  1. body元素浏览器默认给了8px
  2. body元素的margin和test1元素的margin-top发生了margin collapsing(外边距塌陷),所以得到的top值为8px,y值为8px
  3. right的值左边会包含元素的margin-left,但是不会包含margin-right,所以其值等于115,同理bottom也是。

1.3.2、getClientRects()

getClientRects返回一个ClientRect对象集合。提供如下的demo来解释这个方法:

可以在demo中可以看到第一个结果因为文字换行的个数得到集合数组的长度。每一行文字的位置信息我们通过打印都可以看到,按照现在显示是两行,结果如下:

{"0":{"x":9,"y":5,"width":512.21875,"height":28,"top":5,"right":521.21875,"bottom":33,"left":9},"1":{"x":8,"y":27,"width":424.3125,"height":28,"top":27,"right":432.3125,"bottom":55,"left":8}}

而div元素的不支持,所以始终得到的结果是一个,这个结果和获取getBoundingClientRect是一样的:

{"0":{"x":9,"y":75,"width":526,"height":50,"top":75,"right":535,"bottom":125,"left":9}}

所以该方法主要用于判断行内元素是否换行,以及行内元素的每一行的位置偏移

2、HTMLElement

HTMLElement中介绍的是HTMLElement可以标识任何HTML元素。除了继承Element之外,它同样有一系列属于自己的位置参数定义。

2.1、offsetHeight/offsetLeft/offsetParent/offsetTop/offsetWidth

对块级元素来说,offsetTopoffsetLeftoffsetWidthoffsetHeight描述了元素相对于offsetParent的边界框。

然而,对于可被截断到下一行的行内元素(如span),offsetTopoffsetLeft描述的是第一个边界框的位置(使用Element.getClientRects()来获取其宽度和高度),而offsetWidthoffsetHeight描述的是边界框的尺寸(使用 Element.getBoundingClientRect 来获取其位置)。

  1. HTMLElement.offsetParent是一个只读属性,返回一个指向最近的(closest,指包含层级上的最近)包含该元素的定位元素。如果没有定位的元素,则 offsetParent 为最近的table, table cell根元素(标准模式下为html;quirks模式下为body)。当元素的 style.display 设置为 "none" 时,offsetParent返回null。offsetParent 很有用,因为offsetTopoffsetLeft都是相对于其内边距边界的。

  2. offsetHeight获取元素的像素高度,包括元素的边框、内边距和元素的水平滚动条(如果存在且渲染的话),不包含:before或:after等伪类元素的高度,且是一个整数。这个属性值会被四舍五入为整数值,如果你需要一个浮点数值,请用element.getBoundingClientRect()

  3. offsetLeft返回当前元素左上角相对于HTMLElement.offsetParent节点的左边界偏移的像素值。

  4. offsetWidth是一个只读属性,返回一个元素的布局宽度。一个典型的(译者注:各浏览器的offsetWidth可能有所不同)offsetWidth是测量包含元素的边框(border)、水平线上的内边距(padding)、竖直方向滚动条(scrollbar)(如果存在的话)、以及CSS设置的宽度(width)的值。

  5. offsetTop为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部的距离

其测试的demo如下:

3、事件中关于位置的信息

当我们使用click事件或者touch事件的时候也同样会涉及到位置信息。Event包含了很多事件的定义,目前我们只关注MouseEventTouchEvent

3.1、MouseEvent

鼠标点击事件,该事件接口可由这些事件触发:click/dbclick/mouseup/mousedown

MouseEvent继承自UIEvent,UIEvent继承自Event

该事件接口我们关注三个参数:button/clientX/clientY/offsetX/offsetY/pageX/pageY/screenX/screenY。它们分别对应的信息是:

  1. button标识哪种按钮被按了,一般有以下5种类型: 0: 标识主按钮触发的,一般指的是左边按钮或者是未初始化的状态 1: 辅助按钮触发的,一般指的是滚轮按钮或者是中间的按个按钮(如果存在的话) 2: 次级按钮触发的,一般指的是右边按钮 3: 第四个按钮,一般指的是浏览器的返回按钮 4: 第五个按钮,一般指的是浏览器的前进按钮

  2. clientX 是只读属性, 它提供事件发生时的应用客户端区域的水平坐标 (与页面坐标不同)。例如,不论页面是否有水平滚动,当你点击客户端区域的左上角时,鼠标事件的 clientX 值都将为 0 。最初这个属性被定义为长整型(long integer),如今CSSOM视图模块将其重新定义为双精度浮点数(double float),clientY类似。

  3. movementX 是只读属性,它提供了当前事件和上一个mousemove事件之间鼠标在水平方向上的移动值。换句话说,这个值是这样计算的 : currentEvent.movementX = currentEvent.screenX - previousEvent.screenX.movementY类似。

  4. offsetX是鼠标点击的位置相对于目标节点内边位置的X坐标,offsetY类似。

  5. pageX 是一个由MouseEvent接口返回的相对于整个文档的x(水平)的坐标。这个属性考虑任何页面的水平方向上的滚动。 起初这个属性被定义为长整型。 CSSOM 视图模块将它重新定位为双浮点数类型。

  6. screenX是只读属性,标识鼠标点击的位置在全局坐标中横轴的偏移位置。screenY类似。

  7. x属性是clientX的简写,值等于clientX.

3.2、TouchEvent

触摸事件,常用于移动端设备。一次触摸可以产生一个Touch对象,TouchEvent中的touches代表了所有当前接触触摸平面的触点的 Touch对象。这次我们着重关注touches这个属性。

touches是一个集合,集合里面的对象包含的属性大致和MouseEvent的是一样。但是也有些特有的属性:

  1. identifier: 此 Touch 对象的唯一标识符. 一次触摸动作(我们值的是手指的触摸)在平面上移动的整个过程中, 该标识符不变. 可以根据它来判断跟踪的是否是同一次触摸过程.

  2. radiusX标识能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径. 这个值的单位和 screenX 相同.radiusY类似

  3. rotationAngle: 它是这样一个角度值:由radiusX 和 radiusY 描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面.

  4. force:手指挤压触摸平面的压力大小, 从0.0(没有压力)到1.0(最大压力)的浮点数.取决于移动设备的支持。

  5. target是触摸最开始触摸到的元素。哪怕在触点移动过程中, 触点的位置已经离开了这个元素的有效交互区域, 或者这个元素已经被从文档中移除. 需要注意的是, 如果这个元素在触摸过程中被移除, 这个事件仍然会指向它, 但是不会再冒泡这个事件到 window 或 document 对象. 因此, 如果有元素在触摸过程中可能被移除, 最佳实践是将触摸事件的监听器绑定到这个元素本身, 防止元素被移除后, 无法再从它的上一级元素上侦测到从该元素冒泡的事件

二者事件的demo如下:

要查看touchEvent的位置信息需要把该demo在Chrome上调为移动端模式,点击一下框即可得到点击的位置信息。

4、最后的总结

属性 类型 说明
clientHeight/clientWidth float 元素内部的高度,只包含内边距也就是padding-top/padding-bottom,width类似
clientLeft/clientTop float 元素的左边框/上边框的宽度
scrollHeight/scrollWidth float 标识元素的内容高度内容宽度
scrollLeft/scrollTop float 可以读取或设置元素滚动条到元素左边/上边的距离
getBoundingClientRect().bottom/top float Y 轴,相对于视口原点(viewport origin)矩形盒子的底部/顶部。
getBoundingClientRect().height/width float 矩形盒子的高度/宽度(等同于 bottom 减 top/right 减 left)。
getBoundingClientRect().left/right float X 轴,相对于视口原点(viewport origin)矩形盒子的左/右侧。
getBoundingClientRect().x/y float X/Y轴横坐标,矩形盒子左边/顶部相对于视口原点(viewport origin)的距离。只读。
offsetHeight/offsetWidth int 元素的像素高度,包括元素的边框、内边距和元素的水平滚动条(如果存在且渲染的话),不包含:before或:after等伪类元素的高度
offsetLeft/offsetTop int 当前元素相对于其 offsetParent 元素的顶部的距离/左上角相对于HTMLElement.offsetParent节点的左边界偏移的像素值
clientX/clientY long 鼠标点击事件发生时的应用客户端区域的水平坐标 (与页面坐标不同)
movementX/movementY float 当前事件和上一个mousemove事件之间鼠标在水平/垂直方向上的移动值
offsetX/offsetY float 鼠标点击的位置相对于目标节点内边位置的X/Y坐标
pageX/pageY float 一个由MouseEvent接口返回的相对于整个文档的x/y(水平/垂直)的坐标
screenX/screenY long 标识鼠标点击的位置在全局(屏幕)坐标中横轴/纵轴的偏移位置
radiusX/radiusY float 能够包围用户和触摸平面的接触面的最小椭圆的水平/纵轴轴(X/Y轴)半径

Tips:

  1. 带有client字眼的属性一般是衡量属性自身的尺寸(不包含不可见区域)
  2. 带有offset字眼的属性一般指的是元素相对于某个参照物的位置信息
  3. 带有page字眼的属性一般指的是元素相对于整个视图包括滚动未见的区域的位置信息
  4. 带有screen字眼的属性一般指的是元素相对于视图不包括不可见区域的位置信息

参考

  1. TouchEvent.touches
  2. Touch