前端开发用javascript正确获取DOM元素的大小
说明:本篇文章的代码都是在Mac操作系统的Chrome浏览器下进行的,因为部分代码的结果会因为操作系统和浏览器的不同而有所变化。
作为一个前端开发者,需要经常与DOM元素打交道;也经常需要根据DOM元素的大小做一些事情。但是如何正确的获取DOM元素的大小呢?
我们获取一个元素的大小,基本上是通过它的clientX
, offsetX
, scrollX
属性来获取的(其中X
表示width
,height
,left
,top
)。具体属性可以看下表:
clientX | offsetX | scrollX |
---|---|---|
clientWidth | offsetWidth | scrollWidth |
clientHeight | offsetHeight | scrollHeight |
clientLeft | offsetLeft | scrollLeft |
clientTop | offsetTop | scrollTop |
我们首先来说一下clientX相关的属性,clientLeft
表示的是一个元素的左边框的宽度;需要注意的是,如果元素里面的内容产生了垂直的滚动条,并且里面文字的书写方向是从右向左的话,那么clientLeft
包括左边的滚动条的宽度(Windows系统下的Chrome,Firefox和Opera浏览器)。我们来实践一下: HTML部分如下:
<div class="clientLeft">这是clientLeft</div>
CSS部分如下:
.clientLeft {
width: 100px;
height: 100px;
border-left: 6px solid black;
padding: 15px;
background-color: #999;
}
JavaScript部分如下:
// 辅助函数
const $ele = function (ele) {
return document.querySelector(ele);
};
const $log = function () {
console.log.apply(this, arguments);
};
// 测试clientX 属性
let clientLeft = $ele('.clientLeft');
$log('clientLeft的值是:' + clientLeft.clientLeft);
我们会看到控制台的输出如下:
clientLeft的值是:6
然后我们修改一下元素clientLeft
的CSS,让它的内容是从右向左排列的,然后让内容超出;我们来试验一下:
.clientLeft {
/* 省略上面已经写过的内容 */
direction: rtl;
/* 你需要将clientLeft的内容再增加一下,让里面的内容可以产生滑动 */
overflow: scroll;
}
然后我们再看一下控制台的输出,这时候如果是Windows操作系统下的Chrome的话,就不会是6这个值了。因为笔者的电脑是Mac操作系统,所以控制台的打印还是6。 下面是我在windows系统里面的chrome浏览器的测试结果:
控制台的输出如下:
clientLeft的值是:23
接下来我们来讨论一下clientTop
属性,clientTop
指的是元素上边框的宽度,我们来测试一下这个属性。 HTML部分如下:
<div class="clientTop">这是clientTop,永远都不要放弃你的梦想</div>
CSS部分如下:
.clientTop {
width: 100px;
height: 100px;
margin-top: 10px;
border-top: 6px solid black;
padding: 15px;
background-color: #999;
}
JavaScript部分如下:
// 测试clientTop 属性
let clientTop = $ele('.clientTop');
$log('clientTop的值是:' + clientTop.clientTop);
我们可以看到,控制台的输出如下:
clientTop的值是:6
接下来我们来讨论clientWidth
和clientHeight
属性的使用;这两个属性,一个是计算DOM元素的宽度,一个是计算高度的。不过这两个属性都只包含元素本身的宽高,以及元素的padding
值,但是不包含元素的border
和margin
的值,我们来测试一下。还需要注意的是,如果元素里面的内容超出了容器的宽度或者高度,产生了滑动条,那么也不会计算滑动条的部分。 HTML部分如下:
<div class="clientWidth"> 这是clientWidth,永远都不要放弃你的梦想</div>
CSS部分如下:
.clientWidth {
width: 100px;
height: 100px;
margin-top: 10px;
padding: 15px;
background-color: #999;
}
JavaScript部分如下:
// 测试clientWidth 属性
let clientWidth = $ele('.clientWidth');
$log('clientWidth的值是:' + clientWidth.clientWidth);
我们会看到控制台的输出如下:
clientWidth的值是:130
这是因为我们的CSS设置元素的宽度为100px,并且元素的padding
值为15px;所以这个元素的clientWidth
值就是100+15*2=130
。接下来我们来测试一下内容超出容器宽度的情况。 CSS部分如下:
.clientWidth {
/* 以下部分是测试内容超出的情况 */
white-space: nowrap;
overflow: scroll;
}
当我们刷新页面,看到控制台的输出依然是:
clientWidth的值是:130
这是因为clientWidth
返回的是元素的可见部分的宽度,所以如果里面内容有超出的话,是不会计算超出的这部分内容的宽度的;clientHeight
属性和clientWidth
属性相似,这里就不在详细讲解了。 需要说明的是,如果上面的情况是在windows的chrome里面的话,那么clientWidth
的值会变小,因为产生了垂直的滚动条;所以我们实际看到的clientWidth
部分变小了,实际就是原来的值减去垂直滑动条的宽度;所以以后我们在开发的时候也需要多注意这些问题。 下图是我在windows上面的chrome上测试的一部分截图:
关于元素的clientX
相关的属性我们暂时就先介绍到这里,接下来我们来介绍DOM元素的offsetX
相关的属性。在讲解这些属性之前我们需要知道一个元素的offsetParent
表示的是什么,详细的解释可以看这里 HTMLElement.offsetParent ,所谓的offsetParent
指的就是包含当前元素的最近的定位元素。如果没有定位的元素,那么就是最近的table
,table cell
或者根元素(标准模式下为html;quirks模式下为body)。
我们来测试一下一个元素的offsetParent
属性,如下所示: HTML部分如下:
<div class="testWrapper">
<div class="test">用来测试test的offsetParent元素</div>
</div>
CSS部分如下:
.testWrapper {
margin-top: 15px;
position: relative;
}
// 测试offsetParent 属性
let test = $ele('.test');
let testWrapper = $ele('.testWrapper');
$log('test的offsetParent的值是:', test.offsetParent === testWrapper);
控制台的打印结果如下:
test的offsetParent的值是: true
如果不把testWrapper
元素的position
属性设置为relative
或者absolute
的话,那么test
的offsetParent
属性就不再是testWrapper
了。 接下来我们来研究一下offsetLeft
和offsetTop
这两个属性的值,其中offsetLeft
表示当前元素的左上角距离它的offsetParent
元素节点的左边界的距离;而offsetTop
表示的是当前元素的左上角距离它的offsetParent
元素节点的上边界的距离。接下来我们来测试一下: CSS部分如下:
.testWrapper {
margin-top: 15px;
position: relative;
width: 200px;
height: 200px;
padding: 10px;
background-color: #999999;
border: 3px solid #333;
}
.test {
width: 80px;
height: 80px;
float: right;
padding: 5px;
background-color: #ccff33;
margin-top: 36px;
border: 8px solid #333;
}
JavaScript部分如下:
// 测试offsetLeft属性和offsetTop属性
$log('test的offsetLeft的值是:' + test.offsetLeft);
$log('test的offsetTop的值是:' + test.offsetTop);
控制台的输出如下:
test的offsetLeft的值是:104
test的offsetTop的值是:46
因为test
是向右浮动的,所以test
的offsetLeft
的值就是testWrapper
的宽度加上左边的padding
值,然后减去test
的整个宽度(包括border
和padding
的值),也就是200 + 10 - (80 + 5 x 2 + 8 x 2) = 104
,相应的offsetTop
的值就是36 + 10 = 46
(marginTop
的值加上testWrapper
的paddingTop
的值)。 接下来我们来研究一下offsetWidth
和offsetHeight
属性,与clientWidth
和clientHeight
不同的是,offsetWidth
和offsetHeight
不仅包含元素本身CSS设置的宽高,元素内部的padding
值;还包含元素的border
的值,如果内部内容产生了滑动,还包括垂直滑动条的宽度;我们来测试一下。 HTML部分如下:
<div class="offsetWidth"> 这是offsetWidth,永远都不要放弃你的梦想</div>
CSS部分如下:
.offsetWidth {
margin-top: 15px;
width: 100px;
height: 100px;
padding: 8px;
overflow: scroll;
border: 9px solid black;
}
JavaScript部分如下:
// 测试offsetWidth属性
let offsetWidth = $ele('.offsetWidth');
$log('offsetWidth的值是:' + offsetWidth.offsetWidth);
控制台的输出如下:
offsetWidth的值是:134
这个值是由元素的width
值,padding
值以及border
值相加得来的;也就是100 + 8 x 2 + 9 x 2 = 134
。相应的offsetHeight
值也是如此,这里就不在演示了。 接下来我们要研究的是scrollX
相关的属性,首先是scrollLeft
或者scrollTop
属性;这两个属性适用于元素内部有滑动的情况,我们来测试一下scrollLeft
属性。 HTML部分如下:
<div class="scrollLeft">这是scrollLeft,永远都不要放弃你的梦想</div>
CSS部分如下:
.scrollLeft {
width: 100px;
height: 100px;
margin-top: 15px;
white-space: nowrap;
overflow: scroll;
border: 3px solid black;
}
JavaScript部分如下:
// 测试scrollLeft属性
let scrollLeft = $ele('.scrollLeft');
$log('scrollLeft的值是:' + scrollLeft.scrollLeft);
// 修改scrollLeft的值
scrollLeft.scrollLeft = 10;
$log('scrollLeft的值是:' + scrollLeft.scrollLeft);
我们可以看到控制台的输出如下:
scrollLeft的值是:0
scrollLeft的值是:10
在刚开始,因为内容没有进行滑动,所以初始值是0,但是当我们改变了scrollLeft
的值的时候,我们会发现里面内容向左移动,并且scrollLeft
值也变成了我们修改的值。因为scrollTop
和scrollLeft
相似,这里就不再进行演示。 接下来我们来研究一下scrollWidth
和scrollHeight
属性;因为这两个属性比较类似,所以我们就只测试一下scrollWidth
属性。我们接着上面的例子进行测试。 JavaScript部分如下:
// 测试scrollWidth属性的值
$log('scrollWidth的值是:' + scrollLeft.scrollWidth);
我们会看到控制台的输出如下:
scrollWidth的值是:294
因为我们的元素本身的宽度只有100 + 3 x 2 = 106
,但是控制台却输出了294
,这里要说明一下,如果元素内部的内容没有超出元素的宽度,那么scrollWidth
的值就是这个元素的clientWidth
宽度;如果超出了元素的宽度,那么就是元素内容本身的宽度。 到此为止我们基本上把与一个元素大小有关的属性都讲解了一下,但是需要注意的是,因为操作系统和浏览器的实现的不同,元素的同一个属性值可能会不相同。 还有一些注意的点,我在下表中列了出来,我们来一起看一下:
属性 | 读写 | 是否整数 |
---|---|---|
clientLeft | 只读 | 取整(四舍五入) |
clientTop | 只读 | 取整(四舍五入) |
clientWidth | 只读 | 取整(四舍五入) |
clientHeight | 只读 | 取整(四舍五入) |
offsetLeft | 只读 | 取整(四舍五入) |
offsetTop | 只读 | 取整(四舍五入) |
offsetWidth | 只读 | 取整(四舍五入) |
offsetHeight | 只读 | 取整(四舍五入) |
scrollLeft | 读写 | 取整(四舍五入) |
scrollTop | 读写 | 取整(四舍五入) |
scrollWidth | 只读 | 取整(四舍五入) |
scrollHeight | 只读 | 取整(四舍五入) |