一文看懂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設置了邊框和內邊距的話)

注意:元素的偏移量都是只讀的,每次訪問他們都需要重新計算,如果要重復用到這些屬性值,請保存在局部變量中,以提高性能

所以我們在實際開發中,怎樣寫代碼才能將誤差降到最小是我們值得思考的。當然對於偏移量的其他測試沒有全部進行,如果有錯,請告知一聲,我會及時修改。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM