getBoundingClientRect
Overview
Element.getBoundingClientRect() 方法返回元素的宽高(width、height)和其相对于视窗Viewport的位置(top right bottom left),元素与视窗的关系,如下图所示:

需要注意的是在标准盒模型下,元素的 size 是等于width/height + padding + border-width的,如果设置了box-sizing: border-box,元素的 size 即为设置的width/height。
Syntax
domRect = element.getBoundingClientRect();
使用 element.getBoundingClientRect()所返回的 top right bottom left的值会考虑元素在 Viewport 内的滚动操作,当元素发生滚动时,相对视口的位置的值也会发生改变。
非矩形元素
如果元素是一个非矩形的图形,例如元素可能是个五角星、圆或者椭圆之类的非矩形元素,此时返回的结果是以能包含完整元素的最小矩形为基准的。如下图:

width & offsetWidth
在一般情况下,element.getBoundingClientRect().width === element.offsetWidth(height 与 offsetHeight 同理),但是如果元素设置了transform属性,offsetWidth和offsetHeigh的值并不会被影响,依旧等于width/height + padding + border-width。而element.getBoundingClientRect()的所有返回值会返回真实渲染的值。
例如,一个宽高都为 100px 的元素,当为元素设置transform: scale(0.5)时,element.getBoundingClientRect().width的返回值为50px,而offsetWidth的值为依旧为100px。这里有一个小的demo,可以观察下两者的具体区别。
Application
获取元素相对于整个网页左上角定位的属性值
- 水平方向
var x =
this.getBoundingClientRect().left + (window.pageXOffset !== undefined)
? window.pageXOffset
: (document.documentElement || document.body.parentNode || document.body)
.scrollLeft;
- 垂直方向
var y =
this.getBoundingClientRect().top + (window.pageYOffset !== undefined)
? window.pageYOffset
: (document.documentElement || document.body.parentNode || document.body)
.scrollTop;
其中的 window.pageXOffset 和 window.pageYOffset 分别是 window.scrollX 和 window.scrollY的别名,两者是相等的,但是 window.pageXOffset 和 window.pageYOffset 具有较好的兼容性。
window.scrollY的示意图如下:

window.pageXOffset 表示文档/页面水平方向滚动的像素值。其加上元素到视窗的水平距离,可以得到元素到整个网页左上角的水平距离。
window.pageYOffset 表示文档/页面垂直方向滚动的像素值。其加上元素到视窗的垂直距离,可以得到元素到整个网页左上角的垂直距离。
当不支持 window.pageXOffset时,可以使用 document.documentElement.scrollTop(有 DOCTYPE)来获取元素内卷的像素值,如果不支持可以尝试使用 document.body.parentNode.scrollTop 获取,如果还是无效的话,说明 html 文档没有声明 DOCTYPE,此时可以使用 document.body.scrollTop。
判断元素是否在可视区域内
视窗的宽高的计算如下:
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
元素与视图的关系(垂直方向),分为以下几种:
- 元素还没进入到视图中,
top > viewHeight。 - 当元素部分进入视图时,
top > 0但小于视窗的高度viewHeight,此时bottom > 0(如下图图左)。 - 当元素部分移出视图时,
top值为负数,依旧小于视窗的高度viewHeight,此时bottom > 0(如下图图右)。 - 当元素完全离开视图后,
bottom < 0。

根据上面的 2 和 3,判断元素是否在可视区域内很简单,只需要判断元素的 top < viewHeight && bottom > 0 即可。
function isInViewPort(element) {
const {top, bottom} = element.getBoundingClientRect();
const viewHeight =
window.innerHeight || document.documentElement.clientHeight;
return top < clientHeight && bottom > 0;
}
此处只考虑了在垂直方向上,元素是否在视窗内,如果还要考虑水平移动,需要对 left 和 right 进行判断。