DOM元素大小、形状和滚动总结

学 DOM 的知识时一直对元素的大小、形状和滚动相关的那好几个属性傻傻分不清楚,索性专门花一些时间好好专研然后总结下来。

文档坐标、视口坐标

文档坐标 - 滚动量 = 视口坐标

如果文档比视口小,或者还没出现滚动条,那么文档的左上角就是视口的左上角,此时他们坐标的起点是相同的。

鼠标的时间处理函数中,鼠标指针的位置是在视口坐标系中的。

元素大小

偏移量 offset

包括元素在屏幕上占用的所有可见空间(由高度、宽度、内边距、滚动条和边框决定,不包括外边距)。与偏移量相关的 4 个属性:

  • offsetHeight - 元素在垂直方向上占用的空间大小
  • offsetWidth - 水平方向占用的空间大小
  • offsetLeft - 元素的左外边框至包含元素(保存在offsetParent属性中)的左内边框之间的像素距离
  • offetTop - 元素的上外边框至包含元素的上内边框之间的像素距离

要想知道某个元素在页面上的偏移量,将这个元素的offsetLeftoffetTop与其offsetParent的相同属性相加,如此循环直到根元素即可:

1
2
3
4
5
6
7
8
9
10
11
// 对很多元素来说,这是在计算文档坐标
function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;

while (current) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}

注意 - 所有这些偏移量都是只读的,而且每次访问他们都需要重新计算。

客户区大小 client

指的是元素内容及其内边距所占据的空间大小,滚动条占用的空间不算在内。相关属性有 2 个:

  • clientWidth - 元素内容区宽度加上左右内边距宽度,
  • clientHeight - 元素内容区高度加上上下内边距的高度

最常用到这些属性的情况,就是确定浏览器视口大小。要确定浏览器视口大小,可以使用doucment.documentElementdocument.bodyclientWidthclientHeight

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getViewPort() {
// compatMode用来判断当前浏览器采用的渲染方式。
//
if (document.compatMode === 'BackCompat') {
// 标准兼容模式关闭
return {
width: document.body.clientWidth,
height: document.body.clientHeight,
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
};
}
}

注意 - 与偏移量相似,客户区大小也都是只读的,而且每次访问他们都需要重新计算。

滚动大小

指的是包含滚动内容的元素的大小。有些元素(如)能自动添加滚动条,有些则需要通过设置 css 的overflow才能滚动。相关的 4 个属性:

  • scrollHeight - 在没有滚动条的情况下,元素内容的总高度,是元素的内容区加上内边距再加上任何溢出内容的尺寸。
  • scrollWidth - 在没有滚动条的情况下,元素内容的总宽度,是元素的内容区加上内边距再加上任何溢出内容的尺寸。
  • scrollLeft - 被隐藏在内容区左侧的像素数。通过设置这个属性可以改变元素的滚动位置
  • scrollTop - 被隐藏在内容区上方的像素数。通过设置这个属性可以改变元素的滚动位置

scrollHeightscrollWidth主要用于确定元素内容的实际大小。例如通常认为元素是在浏览器视口中滚动的元素,因此带有垂直滚动条的页面总高度就是document.documentElement.scrollHeightdocument.body.scrollHeight
此时浏览器的垂直滚动条高度就是document.documentElement.scrollTopdocument.body.scrollTop

在确定文档总高度时(包括基于视口的最小高度时),必须取得scrollWidth/clientWidthscrollHeight/clientHeight中的最大值,才能保证在跨浏览器的环境下得到精确的结果。

1
2
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth); // 或者document.body
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight); // 或者document.body

通过scrollLeftscrollTop既可以确定元素当前滚动的状态,也可以设置元素的滚动位置。例如在元素尚未被滚动时,这两个属性均为 0.如果元素被垂直滚动了,那么scrollTop的值会大于 0,且表示元素上方不可见内容的像素高度。将元素的scrollTop设置为 0,就可以重置元素的垂直滚动位置。

1
2
3
4
5
6
// 如果元素不是位于顶部,将其回滚到顶部
function scrollToTop(element) {
if (element.scrollTop !== 0) {
element.scrollTop = 0;
}
}

滚动

除了上面说到的设置scrollLeftscrollTop来让元素滚动外,window 对象的scrollTo()(和其同义词scroll())也可以做到,它接收一个文档坐标并作为滚动条的偏移量去设置他们,也就是说窗口滚动到指定的点出现的视口的左上角。

以下代码可以让浏览器滚动到文档最底部可见:

1
2
3
var docHeight = document.documentElement.offsetHeight;
var viewportHeight = getViewPort().height; // 见上面客户区大小的方法
window.scrollTo(0, docHeight - viewportHeight);

window 的scrollBy()scrollTo()scroll()类似,不过他的参数是相对的,并在当前滚动条的偏移量上增加。 例如每秒向下滚动 10 像素:

1
2
3
setInterval(function() {
scrollBy(10);
}, 1000);

在元素上调用scrollIntoView会让浏览器滚动到此元素可见的位置,默认情况下它试图将元素的上边缘放在或尽量接近视口的上边缘,如果只传递 false 作为参数,它将试图将元素的下边缘放在或者尽量接近视口的下边缘。

确定元素大小

getBoundingClientRect方法会返回一个矩形对象,包含 4 个属性: lefttoprightbottom.这些属性是元素在页面中相对于视口的位置,即视口坐标。其返回的坐标包含边框和内边距,但不包含外边距。另外,这个方法返回的坐标是一个快照版本,不会实时更新。

为了将其转化为文档坐标,需要加上浏览器滚动条的大小(见上面滚动大小小节)。

参考

本文内容均是参考自JavaScript红宝书(第 12 章)和权威指南(15.8)的。