offsetParent、offsetLeft/offsetTop深度剖析

Codeeeee發表於2018-12-22

element.offsetParent

定義

element.offsetParent為包含element的祖先元素中,層級最近的定位元素。也就是說,offsetParent必須滿足三個條件:

  • 是element的祖先元素
  • 最靠近element
  • 是定位元素,即position屬性不為static
<
div class="position-outer" style="position: relative;
"
>
<
div class="position" style="postion: relative;
"
>
<
div class="not-position">
<
div class="box">
<
/div>
<
/div>
<
/div>
<
/div>
複製程式碼

列印box元素的offsetParent:

offsetParent、offsetLeft/offsetTop深度剖析

由此可見,box祖先元素中存在:

  • 層級為3的定位元素 position-outer
  • 層級為2的定位元素 position-inner
  • 層級為1的非定位元素 not-position

position-inner是同時滿足層級最近和定位兩個條件的。

祖先元素中不存在定位元素

<
div class="box">
<
/div>
複製程式碼
offsetParent、offsetLeft/offsetTop深度剖析

webkit核心、Firefox下的特殊情況

  • element自身的display屬性為none
<
div class="position-outer" style="position: relative;
"
>
<
div class="position" style="postion: relative;
"
>
<
div class="not-position">
<
div class="box" style="display: none;
"
>
<
!-- 注意這裡! -->
<
/div>
<
/div>
<
/div>
<
/div>
複製程式碼
offsetParent、offsetLeft/offsetTop深度剖析
  • element自身的position屬性為fixed
<
div class="position-outer" style="position: relative;
"
>
<
div class="position" style="postion: relative;
"
>
<
div class="not-position">
<
div class="box" style="position: fixed;
"
>
<
!-- 注意這裡! -->
<
/div>
<
/div>
<
/div>
<
/div>
複製程式碼
offsetParent、offsetLeft/offsetTop深度剖析

element.offsetWidth / element.offsetHeight

定義

offsetWidth = content + (垂直滾動條的寬度) + padding + border

  • 無滾動條情況下
<
div class="box" style="">
<
/div>
複製程式碼
.box { 
width: 200px;
height: 100px;
padding: 20px;
border: 12px solid red;
margin: 25px;

}複製程式碼
offsetParent、offsetLeft/offsetTop深度剖析

列印element.offsetWidth:

offsetParent、offsetLeft/offsetTop深度剖析

offsetWidth = 200(content) + 20 * 2(padding) + 12 * 2(border) = 264

  • 有滾動條情況下
offsetParent、offsetLeft/offsetTop深度剖析

可以看到,滾動條包含在padding中,因此,offsetWidth與在無滾動條情況下,大小不變。

element.offsetLeft / element.offsetTop

定義

element左上角相對於offsetParent左邊界的偏移值。

有個疑問?element的左上角 與 offsetParent的左邊界如何定義?是content-box、padding-box還是margin-box?

<
div class="position">
<
div class="box">
<
/div>
<
/div>
複製程式碼
.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

offsetParent、offsetLeft/offsetTop深度剖析

列印box.offsetLeft和box.offsetTop:

offsetParent、offsetLeft/offsetTop深度剖析

在文件流中,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屬性:

.box { 
position: relative;
/* 新增的 */ top: 31px;
/* 新增的 */ left: 31px;
/* 新增的 */ width: 200px;
height: 100px;
padding: 20px;
border: 12px solid red;
margin: 25px;

}複製程式碼

再次列印box.offsetLeft和box.offsetTop:

offsetParent、offsetLeft/offsetTop深度剖析

兩者的值都增加了31,也就是top和left屬性對應的值,由此更新計算公式:

element.offsetLeft = offsetParent.paddingLeft + element.left + element.marginLeft

但是,以上都是在最簡單的情況下計算的,即element與element.offsetParent之間沒有其它層級的元素存在!

我們在element與element.offsetParent之間再插入一個元素:

<
div class="position">
<
div class="middle">
<
!-- 新增的 -->
<
div class="box">
<
/div>
<
/div>
<
/div>
複製程式碼
.parent { 
width: 30px;
height: 150px;
padding: 11px;
border: 12px solid pink;
margin-left: 13px;

}複製程式碼
offsetParent、offsetLeft/offsetTop深度剖析

列印box.offsetLeft和box.offsetTop:

offsetParent、offsetLeft/offsetTop深度剖析

兩者的值又變化了!相比上次又增加了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向右浮動:

.parent { 
float: right;
width: 30px;
height: 150px;
padding: 11px;
border: 12px solid pink;
margin-left: 13px;

}複製程式碼

列印box.offsetLeft和box.offsetTop:

offsetParent、offsetLeft/offsetTop深度剖析

這次只有box.offsetLeft變化了,而且也可以猜測到是由於parent的右浮,box是其子元素,一起右浮,導致box.offsetLeft變化的。

這下要怎麼計算?難道還要算浮動的距離嗎?

並不需要!只要藉助parent就能計算!仔細想想,element與element.offsetParent一定是沒有浮動元素的,那麼對於parent,其offsetParent也就是box的offsetParent,即postition。

我們列印下parent.offsetleft:

offsetParent、offsetLeft/offsetTop深度剖析

再計算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:

<
div class="position">
<
div class="box">
<
/div>
<
/div>
複製程式碼
.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:

offsetParent、offsetLeft/offsetTop深度剖析

我們知道,display屬性為absolute或fixed的元素,是相對於包含塊的padding-box定位的,因此在計算offsetLeft時,就不需要考慮offsetParent的paddingLeft了。

並且,element是脫離文件流的,也就是說,除了element.offsetParent,不再與其它任何元素產生聯絡,也就不需要再考慮element與element.offsetParent之間的任何元素了。

因此,計算公式非常簡單:

element.offsetLeft = element.left + element.marginLeft

來源:https://juejin.im/post/5c1dd37df265da6112050014

相關文章