offsetParent与offsetTop和offsetLeft
element.offsetParent
element.offsetParent 为包含 element 的祖先元素中,层级最近的定位元素。 也就是说,offsetParent 必须满足三个条件:
- 是 element 的祖先元素
- 最靠近 element
- 是定位元素,即 position 属性不为 static
html
<div class="position-outer" style="position: relative;">
<div class="position-inner" style="postion: relative;">
<div class="not-position">
<div class="box"></div>
</div>
</div>
</div>
祖先元素中不存在定位元素
则 offsetParent 是 body
html
<div class="box"></div>
webkit 内核、Firefox 下的特殊情况
element 自身的 display 属性为 none
则
offsetParent为null
html
<div class="position-outer" style="position: relative;">
<div class="position-inner" style="postion: relative;">
<div class="not-position">
<div class="box" style="display: none;"><!-- 注意这里! --></div>
</div>
</div>
</div>
element 自身的 position 属性为 fixed
则
offsetParent为null
html
<div class="position-outer" style="position: relative;">
<div class="position-inner" style="postion: relative;">
<div class="not-position">
<div class="box" style="position: fixed;"><!-- 注意这里! --></div>
</div>
</div>
</div>
element.offsetWidth / element.offsetHeight
TIP
offsetWidth = content + (垂直滚动条的宽度) + padding + border
无滚动条情况下
html
<div class="box" style=""></div>
css
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 12px solid red;
margin: 25px;
}

打印element.offsetWidth: 264
offsetWidth = 200(content) + 20 * 2(padding) + 12 * 2(border) = 264
有滚动条情况下
可以看到,滚动条包含在 padding 中,因此,offsetWidth 与在无滚动条情况下,大小不变。
element.offsetLeft / element.offsetTop
element 左上角相对于 offsetParent 左边界的偏移值。
有个疑问? element 的左上角 与 offsetParent 的左边界如何定义?是 content-box、padding-box 还是 margin-box?
html
<div class="position">
<div class="box"></div>
</div>
css
.position {
position: relative;
top: 0;
left: 0;
width: 400px;
height: 200px;
padding: 35px;
border: 15px solid purple;
}
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 12px solid red;
margin: 25px;
}
很明显,box 的 offsetParent 为 postion

打印 box.offsetLeft 和 box.offsetTop:
offsetLeft: 60
offsetTop: 60
在文档流中,box 的整个 margin-box 是在 position 的 content-box 中的,由此可猜测: box.offsetLeft = position.paddingLeft + box.marginleft = 35 + 25 = 60 真的是这样吗?其实并没有那么简单,需要分两种情况讨论:
element 在正常文档流中
element.offsetLeft 是指 element 的 border-box 左上角相对 offsetParent 的 content-box 的偏移量
由于position: relative的元素并没有脱离文档流,因此,也需要加入到offseLeft/offsetTop的计算中
修改 box css 属性:
css
.box {
position: relative; /* 新增的 */
top: 31px; /* 新增的 */
left: 31px; /* 新增的 */
width: 200px;
height: 100px;
padding: 20px;
border: 12px solid red;
margin: 25px;
}
再次打印box.offsetLeft和box.offsetTop:
offsetLeft: 91
offsetTop: 91
两者的值都增加了 31,也就是 top 和 left 属性对应的值,由此更新计算公式:
element.offsetLeft = offsetParent.paddingLeft + element.left + element.marginLeft
但是,以上都是在最简单的情况下计算的,即 element 与 element.offsetParent 之间没有其它层级的元素存在!
我们在 element 与 element.offsetParent 之间再插入一个元素:
html
<div class="position">
<!-- 新增的 -->
<div class="middle">
<div class="box"></div>
</div>
</div>
css
.parent {
width: 30px;
height: 150px;
padding: 11px;
border: 12px solid pink;
margin-left: 13px;
}

打印 box.offsetLeft 和 box.offsetTop:
offsetLeft: 127
offsetTop: 127
两者的值又变化了!相比上次又增加了 36,正好是 parent 的 marginLeft、borderLeft 、paddingLeft 之和,11 + 12 + 13 = 36,由此得到最终的计算公式:
element.offsetLeft = offsetParent.paddingLeft + element.left + element.marginLeft + (element与element.offsetParent之间所有 在正常文档流且position属性不为relative的元素 marginLeft、borderLeft、paddingLeft之和)
element 与 element.offsetParent 之间存在浮动元素
现在,我们让 parent 向右浮动:
css
.parent {
float: right;
width: 30px;
height: 150px;
padding: 11px;
border: 12px solid pink;
margin-left: 13px;
}
打印 box.offsetLeft 和 box.offsetTop:
offsetLeft: 168
offsetTop: 114
这次只有 box.offsetLeft 变化了,而且也可以猜测到是由于 parent 的右浮,box 是其子元素,一起右浮,导致 box.offsetLeft 变化的。
这下要怎么计算?难道还要算浮动的距离吗?
并不需要!只要借助 parent 就能计算!仔细想想,element 与 element.offsetParent 一定是没有浮动元素的,那么对于 parent,其 offsetParent 也就是 box 的 offsetParent,即 postition。 我们打印下 parent.offsetleft:
parent.offsetLeft: 76
再计算 box 到 parent 之间的偏移量: box.left + box.marginLeft + parent.paddingLeft + parent.borderLeft + parent.marginLeft = 31 + 25 + 11 + 12 + 13 = 92
76 + 92 = 168,与 box.offsetLeft 一致,这也说明我们的计算公式是正确的!
element 脱离文档流
也就是说 element 的 display 属性为 absolute 或 fixed,由于 fixed 会导致 offsetParent 为 null,所以我们将 box 的 display 设置为 absolute:
html
<div class="position">
<div class="box"></div>
</div>
css
.box {
position: absolute; /* 新增的 */
top: 31px;
left: 31px;
width: 200px;
height: 100px;
padding: 20px;
border: 12px solid red;
margin: 25px;
}
.position {
position: relative;
top: 0;
left: 0;
width: 400px;
height: 200px;
padding: 35px;
border: 15px solid purple;
}
打印 box.offsetLeft 和 box.offsetTop:
offsetLeft: 191
offsetTop: 91
我们知道,display 属性为 absolute 或 fixed 的元素,是相对于包含块的 padding-box 定位的,因此在计算 offsetLeft 时,就不需要考虑 offsetParent 的 paddingLeft 了。
并且,element 是脱离文档流的,也就是说,除了 element.offsetParent,不再与其它任何元素产生联系,也就不需要再考虑 element 与 element.offsetParent 之间的任何元素了。
因此,计算公式非常简单:
element.offsetLeft = element.left + element.marginLeft