一文看懂js中元素偏移量(offsetLeft,offsetTop,offsetWidth,offsetHeight)
偏移量(offset dimension)
偏移量:包括元素在屏幕上占用的所有可见空间,元素的可见大小有其高度,宽度决定,包括所有内边距,滚动条和边框大小(注意,不包括外边距)。
以下4个属性可以获取元素的偏移量
1. offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度(可见的),水平滚动条的高度,上边框高度和下边框高度。
2. offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度(可见的),垂直滚动条的宽度,左边框宽度和右边框宽度。
3: offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
4: offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
其中offsetLeft,offsetTop属性与包含元素有关,包含元素的引用保存在offsetParent中,请注意offsetParent与parentNode的值不一定相等。
有了理论基础,那我们就要动手看看,看看事实是不是真的是那样:
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
} .parent {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: red;
border: 10px solid #000;
overflow: hidden;
} .child {
width: 300px;
height: 300px;
border: 1px solid #000;
padding: 10px;
margin: 50px 90px;
background-color: green;
}
</style>
</head> <body>
<div class="parent">
<div class="child"></div>
</div>
<script>
var child = document.querySelector('.child');
var html = '';
html += "offsetWidth=" + child.offsetWidth + "<br>";
html += "offsetHeight=" + child.offsetHeight + "<br>";
html += "offsetTop=" + child.offsetTop + "<br>";
html += "offsetLeft=" + child.offsetLeft;
child.innerHTML = html;
</script>
</body> </html>
在查看结果之前我们按照自己对理论的理解,先猜测以下会出现什么结果。
class名称为child的相关偏移量猜测结果如下:(猜测前提,child是包含在parent中的)
offsetWidth = 10*2(左右内边距) + 1*2(左右边框)+ 300(元素的宽度)+ 0(垂直滚动条的高度)= 322;
offsetHeight = 10*2(上下内边距)+1*2(上下边框)+300 (元素的高度)+ 0(水平滚动条的高度)= 322;
offsetLeft = 90 (元素的上外边框至包含元素的上内边框之间的像素距离,在此为左外边的距离);
offsetTop = 50 (元素的上外边框至包含元素的上内边框之间的像素距离,在此为上外边的距离);
在看看下实际的结果:

不难发现offsetWidth和offsetHeight与我们猜测的一致,但是其他两个属性出现较大偏差,原因如下:
offsetParent:是指元素最近的定位(relative,absolute)祖先元素,如果没有祖先元素是定位的话,会指向body元素。
现修改parent样式如下:
.parent {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: red;
border: 10px solid #000;
overflow: hidden;
position: relative;
}
再次查看结果:

现在结果和我们的猜测一致。所以在使用offsetLeft和offsetTop的时候,一定要注意offsetParent的指向。
接下来我们做如下几个测试:(为了测试方便,指定child的offsetParent为parent)
1. 元素的盒模型对以上四个属性有什么影响?
修改child的css代码如下:
.child {
width: 300px;
height: 300px;
border: 1px solid #000;
padding: 10px;
margin: 50px 90px;
background-color: green;
box-sizing: border-box;
}
查看结果:

发现改变盒模型会对元素的offsetWidth盒offsetHeight产生影响.
2. 元素定位对上面四个属性的影响?
修改css代码如下:
.parent {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: red;
border: 10px solid #000;
overflow: hidden;
position: relative;
}
.child {
position: absolute;
top: 30px;
left: 50px;
width: 300px;
height: 300px;
border: 1px solid #000;
padding: 10px;
margin: 50px 90px;
background-color: green;
box-sizing: border-box;;
}
查看结果:

发现offsetTop多了30(这30就是child相对于parent绝对定位top方向上的方位值),offsetLeft多了50(这50就是child相对于parent绝对定位left方向上的方位值)。
可以的出元素的偏移量中的offsetTop,offsetLeft与在offsetParent中的外边距和定位的方位值有关系。
现给出计算元素在页面上的offsetLeft和offsetTop(注意,不仅仅是在包含元素中的值)
var getOffset = {
left: function (element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
},
top: function (element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
}
在这里我们递归寻找元素的offsetParent,然后在一直加上offsetParent中的offsetTop或者offsetLeft,最终得到元素在页面中的offsetLeft和offsetTop。乍一看很完美是吧,但是两个函数只能得到一个基本准确的值,为什么这么说了,通过下面几个案例就可以得到答案。
测试案例1代码如下:
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
} .parent {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: red;
border: 10px solid #000;
overflow: hidden;
} .child {
width: 300px;
height: 300px;
border: 10px solid #000;
padding: 10px;
margin: 50px 90px;
background-color: green;
}
</style>
</head> <body>
<div class="parent">
<div class="child"></div>
</div>
<script>
var child = document.querySelector('.child');
var html = '';
html += "offsetWidth=" + child.offsetWidth + "<br>";
html += "offsetHeight=" + child.offsetHeight + "<br>";
html += "offsetTop=" + child.offsetTop + "<br>";
html += "offsetLeft=" + child.offsetLeft;
child.innerHTML = html;
</script>
</body> </html>
在这里child的offsetParent为body元素,就以此结果为元素在页面上的偏移量的准确结果。
查看结果:

请记住这个结果,下面我们用定义的方法来获取child的offsetTop和offsetLeft。
测试案例2代码如下:
<script>
var child = document.querySelector('.child');
var html = '';
var getOffset = {
left: function(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while(current) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
},
top: function(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
while(current) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
}
html += "offsetWidth=" + child.offsetWidth + "<br>";
html += "offsetHeight=" + child.offsetHeight + "<br>";
html += "offsetTop=" + getOffset.top(child) + "<br>";
html += "offsetLeft=" + getOffset.left(child);
child.innerHTML = html; </script>
</body>
在这里没有直接使用child元素的offsetLeft和offsetTop,而是通过所定义的函数计算出来的。
结果如下:

结果一样,是不是说明那个函数计算出来的就是准确的结果了?,继续往下看。
测试案例3代码如下:
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
} .parent {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: red;
border: 10px solid #000;
overflow: hidden;
/*注意这里 相当于把child的offsetParent设置为了parent*/
position: relative;
} .child {
width: 300px;
height: 300px;
border: 10px solid #000;
padding: 10px;
margin: 50px 90px;
background-color: green;
}
</style>
</head> <body>
<div class="parent">
<div class="child"></div>
</div>
<script>
var child = document.querySelector('.child');
var html = '';
var getOffset = {
left: function (element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
},
top: function (element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
}
html += "offsetWidth=" + child.offsetWidth + "<br>";
html += "offsetHeight=" + child.offsetHeight + "<br>";
html += "offsetTop=" + getOffset.top(child) + "<br>";
html += "offsetLeft=" + getOffset.left(child);
child.innerHTML = html; </script>
</body> </html>
请注意parent样式注释的部分,现在看看结果:

发现是不是在页面上的offsetLeft和offsetTop相对于精确结果来说都少了10,为什么会出现这个现象?其中从offsetLeft和offsetTop的定义中就可以得到答案:
offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
在这例子中我们把parent设置了child的offsetParent,根据定义我们可以得出child在parent中的offsetTop为50而parent在body中的offsetTop为100(offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离)所以在计算中我们忽略了parent的边框高度(为10px)所以就出现了10px的误差,offsetLeft的10误差解释也是一样。
在来看一个案例:
测试案例4代码如下:
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
} .parent {
width: 500px;
height: 500px;
margin: 100px auto;
background-color: red;
border: 10px solid #000;
overflow: hidden;
/*注意这里*/
box-sizing: border-box;
} .child {
width: 300px;
height: 300px;
border: 10px solid #000;
padding: 10px;
margin: 50px 90px;
background-color: green;
}
</style>
</head> <body>
<div class="parent">
<div class="child"></div>
</div>
<script>
var child = document.querySelector('.child');
var html = '';
var getOffset = {
left: function (element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
},
top: function (element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
}
html += "offsetWidth=" + child.offsetWidth + "<br>";
html += "offsetHeight=" + child.offsetHeight + "<br>";
html += "offsetTop=" + getOffset.top(child) + "<br>";
html += "offsetLeft=" + getOffset.left(child);
child.innerHTML = html; </script>
</body> </html>
注意看parent样式注释的地方。
结果如下:

发现child在页面上的offsetLeft比标准结果多了10,因为改变了parent的盒模型,导致parent原先向外扩充的边框变成向内填充,其边框长度为10,所以导致offsetLeft的结果多了10。
到这里可以发现上面计算元素在页面的offsetLeft和offsetTop确实是存在一定误差的,相关因素如下:
1. 修改元素的offsetParent
2.修改offetParent的盒模型(如果offsetParent设置了边框和内边距的话)
注意:元素的偏移量都是只读的,每次访问他们都需要重新计算,如果要重复用到这些属性值,请保存在局部变量中,以提高性能
所以我们在实际开发中,怎样写代码才能将误差降到最小是我们值得思考的。当然对于偏移量的其他测试没有全部进行,如果有错,请告知一声,我会及时修改。
一文看懂js中元素偏移量(offsetLeft,offsetTop,offsetWidth,offsetHeight)的更多相关文章
- 一文看懂js中元素的滚动大小(scrollWidth,scrollHeight,scrollTop,scrollLeft)
滚动大小(scroll dimension) 滚动大小指的是包含滚动内容元素的大小. 以下是与元素滚动内容大小相关的属性: 1. scrollWidth:在没有滚动条的情况下,元素内容的总宽度. 2. ...
- 一文看懂js中元素的客户区大小(clientWidth,clientHeight)
元素的客户区 元素的客户区大小,指的是元素内容及其内边距所占据的空间大小. 相关属性如下: 1. clientWidth:元素内容区宽度+元素左右内边距 2. clientHeight:元素内容区高度 ...
- 一文看懂js中的clientX,clientY,pageX,pageY,screenX,screenY
一. 客户区坐标位置(clientX,clientY) 鼠标事件都是在浏览器视口中的特定位置发生的.这个位置信息保存在事件对象的clientX和clientY属性中,所有浏览器都支持这两个属性. 我们 ...
- 一文搞懂 js 中的各种 for 循环的不同之处
一文搞懂 js 中的各种 for 循环的不同之处 See the Pen for...in vs for...of by xgqfrms (@xgqfrms) on CodePen. for &quo ...
- 一文看懂JS继承
继承是OOP中大家最喜欢谈论的内容之一,一般来说,继承都两种方式:接口继承和实现继承而JavaScript中没有接口继承需要的方法,因此只能依靠实现继承.在讲继承的实现之前,我们首先来回顾一下什么是继 ...
- js中的offsetParent,offsetLeft,offsetTop及jquery的offset(),position()比较
1.offsetParent 元素的offsetParent并不是元素的父元素,判断元素的offsetParent要根据以下情况: 1)当DOM结构层次中的元素均没有进行css定位(设置positio ...
- 一文读懂JS中的原型和原型链(图解)
讲原型的时候,我们应该先要记住以下几个要点,这几个要点是理解原型的关键: 1.所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外). 2.所有的引用类型都有一个’_ _ proto_ ...
- 一文搞懂js中的typeof用法
基础 typeof 运算符是 javascript 的基础知识点,尽管它存在一定的局限性(见下文),但在前端js的实际编码过程中,仍然是使用比较多的类型判断方式. 因此,掌握该运算符的特点,对于写出好 ...
- 【转帖】一文看懂docker容器技术架构及其中的各个模块
一文看懂docker容器技术架构及其中的各个模块 原创 波波说运维 2019-09-29 00:01:00 https://www.toutiao.com/a6740234030798602763/ ...
随机推荐
- 研究NLP100篇必读的论文---已整理可直接下载
100篇必读的NLP论文 100 Must-Read NLP 自己汇总的论文集,已更新 链接:https://pan.baidu.com/s/16k2s2HYfrKHLBS5lxZIkuw 提取码:x ...
- winform 界面加载慢原因分析
公司新来的开发人员,对winform开发还不是特别精通,在做个性化界面体验的时候容易出现闪烁和加载慢 闪烁的话,通过winform窗体的双缓存来解决在form 窗体中增加如下代码 protected ...
- L3-016 二叉搜索树的结构 (30 分)
二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值:若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值:它的左.右子树也分别 ...
- 绿洲作业第一周 - Y3每日中文学习任务清单
尽量每天完成一首古诗. 如果孩子另有学习安排,可在周日(2.16)23:59前,完成任务.有兴趣.学有余力的孩子可以自我拓展其中的“寒假读诗”.
- slqmap简单使用
网址:-u 指定哪个参数:-p 需要登录用--cookie 获得所有数据库:--dbs 获得所有用户:--users 指定某个数据库:-D 显示所有表--tables 指定某个数据表:-T 显示列:- ...
- python之os和sys模块的区别
一.os模块 os模块是Python标准库中提供的与操作系统交互的模块,提供了访问操作系统底层的接口,里面有很多操作系统的函数 1.os常用方法 import os # print(os.getcwd ...
- 收集到的技术相关网址——python
1.Python中常用数据库访问接口模块 专用数据库连接模块——MySQL.SQLite.PostgreSQL.Oracle.IBM DB2.Infomix.Interbase.Sybase.SQL ...
- left join on和where 限制查询的区别在于
left join on: 会显示前表的所有数据,不满足显示为null或者为0 . 而where显示的为满足条件的记录,不满足但是存在的数据不显示. 做统计数据的时候,用join on比较合理.
- 13.1 CentOS系统启动流程介绍
CentOS系统启动流程介绍 本章我们将学习 Linux 启动流程和内核模块管理相关的内容.通过本章我们将学习如下内容: Linux 系统的组成及特点 CentOS 系统的启动流程 开机启动成 gru ...
- volatile与Synchronized
摘自: https://blog.csdn.net/zxh476771756/article/details/78685581 一.JVM内存模型: JVM将内存组织为主内存和工作内存两个部分. 主内 ...