DOMElement之Offset

有明確目的的學習比較有效,我學習HTML offset相關概念是出自一個需求,那就是計算一個絕對定位的HTML元素相對於當前窗口的偏移距離,主要是Y方向的偏移,X方向同理。html

要實現這個目的,首先要弄清楚HTML關於偏移距離的定義:node

1. offsetTopjquery

MDN的定義:The HTMLElement.offsetTop read-only property returns the distance of the current element relative to the top of the offsetParent node.app

既然是distance,就涉及到比較的兩個點,起點和終點(或者是兩條平行線)。根據CSS盒子模型,HTMLElement元素有margin, border, padding和content. 計算的是margin edge到margin edge呢,border edge到border edge呢,是padding edge到padding edge呢,仍是content edge到content edge呢?甚至是一個元素的padding edge到另一個元素的margin edge呢?ide

這裏全部的邊,都是指代外面的邊,以下圖所示。學習

CSS Box model

注:圖片來自MDN測試

問題1:offsetTop計算的是當前元素相對於它的offsetParent元素的頂部的距離,那麼是哪條邊到哪條邊的距離呢?ui

在回答這個問題以前,咱們得先弄清楚offsetParent是個什麼東西。this

2. offsetParentspa

MDN的定義:The HTMLElement.offsetParent read-only property returns a reference to the object which is the closest (nearest in the containment hierarchy) positioned containing element. If the element is non-positioned, the nearest table cell or root element (html in standards compliant mode; body in quirks rendering mode) is the offsetParentoffsetParent returns null when the element has style.displayset to "none". The offsetParent is useful because offsetTop and offsetLeft are relative to its padding edge.

closet (nearest in the containment hierarchy) positioned containing element - 在包含層次結構(也就是當前元素的祖先元素)中離當前元素最近的,定位過的元素。也就是說是當前元素的父級(祖先)元素中,離當前元素最近的定位過的元素。那麼又出現一個新的問題,什麼是「定位過的元素」

3. position

MDN的定義:The position CSS property chooses alternative rules for positioning elements, designed to be useful for scripted animation effects.

Position是一個CSS屬性,來定義元素的定位規則,有下面一些值能夠選擇,一下內容來自w3.org,由於MDN聲稱有sticky一值可選,通過測試Chrome和IE都不支持,只有Firefox支持,此屬性尚屬於CSS3 draft階段。

static The box is a normal box, laid out according to the normal flow. The 'top''right''bottom', and 'left' properties do not apply.

relative The box's position is calculated according to the normal flow (this is called the position in normal flow). Then the box is offset relative to its normal position. When a box B is relatively positioned, the position of the following box is calculated as though B were not offset. The effect of 'position:relative' on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined.

absolute The box's position (and possibly size) is specified with the 'top''right''bottom', and 'left' properties. These properties specify offsets with respect to the box's containing block. Absolutely positioned boxes are taken out of the normal flow. This means they have no impact on the layout of later siblings. Also, though absolutely positioned boxes have margins, they do not collapse with any other margins.

fixed The box's position is calculated according to the 'absolute' model, but in addition, the box is fixed with respect to some reference. As with the 'absolute' model, the box's margins do not collapse with any other margins. In the case of handheld, projection, screen, tty, and tv media types, the box is fixed with respect to the viewport and does not move when scrolled. In the case of the print media type, the box is rendered on every page, and is fixed with respect to the page box, even if the page is seen through a viewport (in the case of a print-preview, for example). For other media types, the presentation is undefined. Authors may wish to specify 'fixed' in a media-dependent way. For instance, an author may want a box to remain at the top of the viewport on the screen, but not at the top of each printed page.

回到咱們最初的問題,position是哪一種值纔算是「定位過的元素」呢?

從定義彷佛看不出來什麼,那麼就來作測試吧。

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <style>
 5 #parent_static { position: static; }
 6 #parent_absolute { position: absolute; top: 20px; }
 7 #parent_relative { position: relative; top: 60px; }
 8 #parent_fixed { position: fixed; top: 100px; }
 9 #child_static,#child_absolute,#child_relative,#child_fixed { position:absolute; top: 5px; width: 400px; border: 1px solid;}
10 </style>
11 </head>
12 <body>
13 <div id="parent_static"><div id="child_static"></div></div>
14 <div id="parent_absolute"><div id="child_absolute"></div></div>
15 <div id="parent_relative"><div id="child_relative"></div></div>
16 <div id="parent_fixed"><div id="child_fixed"></div></div>
17 <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
18 <script>
19 function getOffsetParentString(obj)
20 {
21     if(obj.offsetParent)
22     {
23         if(obj.offsetParent.id)
24         {
25             return obj.id + ":\t" + obj.offsetParent.id;
26         }
27         else
28         {
29             return obj.id + ":\t" + obj.offsetParent.tagName;
30         }
31     }
32     else
33     {
34         return "null";
35     }
36 }
37 $('#child_static').html(getOffsetParentString($('#child_static')[0]));
38 $('#child_absolute').html(getOffsetParentString($('#child_absolute')[0]));
39 $('#child_relative').html(getOffsetParentString($('#child_relative')[0]));
40 $('#child_fixed').html(getOffsetParentString($('#child_fixed')[0]));
41 </script>
42 </body>
43 </html>

結果以下:

 

OK,從上面的測試咱們能夠獲得一個結論,除了static方式的position(也是默認方式),其餘的三個元素都是所謂的「positioned」。

那麼回答以前的一個問題:什麼是「定位過的元素」?

定位過的元素就是使用Position:relative|absolute|fixed做爲定位方式的HTML元素。

那麼接着offsetParent的定義也就清楚了,在當前元素的包含結構上的離它最近的定位過的父元素就是它的offsetParent,若是沒有,默認就是HTML的body元素。

讓咱們回到最初的問題,offsetTop計算的是當前元素相對於它的offsetParent元素的頂部的距離,那麼是哪條邊到哪條邊的距離呢?

繼續用事實說話:

<!doctype html>
<html>
<head>
<style>
#parent_absolute { position: absolute; top: 8px;; margin: 1px; border: 2px solid blue; padding: 4px; width: 800px; height: 600px; }
#child_absolute { position:absolute; top: 16px; margin-top: 32px; border: 64px solid #ccc; padding: 128px;}
</style>
</head>
<body>
<div id="parent_absolute"><div id="child_absolute"></div></div>
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
function getOffsetTopString(obj)
{
    if(obj.offsetParent)
    {
        if(obj.offsetParent.id)
        {
            return "OffsetTop of (" + obj.id + " - " + obj.offsetParent.id + "):\t" + obj.offsetTop;
        }
        else
        {
            return "OffsetTop of (" + obj.id + " - " + obj.offsetParent.tagName + "):\t" + obj.offsetTop;;
        }
    }
    else
    {
        return "null";
    }
}
$('#child_absolute').html(getOffsetTopString($('#child_absolute')[0]));
</script>
</body>
</html>

從測試結果能夠看出,offsetTop的值是當前元素的"margin-top"+"top",可是仍是沒法回答咱們以前的那個問題,這個值是哪條邊到哪條邊呢?由於咱們不知道這個值是否覆蓋了父元素的padding甚至是border

有一個解決方案是找一把pixel ruler(好比一個很小巧的免費軟件叫JRuler)在屏幕上測量一下

另一種方法是,把當前元素的margin-top設置爲0px, top設置爲-1px,看看這個元素的border會出如今哪一個位置

把上面的code改爲: 

#child_absolute { position:absolute; top: -1px; margin-top: 0px; border: 64px solid #ccc; padding: 128px;}

 

能夠看到,當前元素覆蓋了offsetParent元素的border一個像素。

那麼如今咱們能夠回答一開始的問題了,offsetTop計算的是當前元素相對於它的offsetParent元素的頂部的距離,那麼是哪條邊到哪條邊的距離呢?

offsetTop是當前元素的border edge到它的offsetParent元素的padding edge的距離

用圖來表示就是

 

 

注1:此圖爲原創

注2:不要誤覺得offsetTop = padding+margin, 圖示所表達的意思是offsetTop是當前元素的border邊界到其offsetParent元素的padding邊界的距離

相關文章
相關標籤/搜索