Skip to content
文章目录

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

offsetParentnull

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

offsetParentnull

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.offsetLeftbox.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

资料来源

offsetParent、offsetLeft/offsetTop 深度剖析

HTMLElement.offsetParent

深入理解定位父级 offsetParent 及偏移大小