zoukankan      html  css  js  c++  java
  • 一文看懂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设置了边框和内边距的话)

    注意:元素的偏移量都是只读的,每次访问他们都需要重新计算,如果要重复用到这些属性值,请保存在局部变量中,以提高性能

    所以我们在实际开发中,怎样写代码才能将误差降到最小是我们值得思考的。当然对于偏移量的其他测试没有全部进行,如果有错,请告知一声,我会及时修改。

  • 相关阅读:
    小孩抓周
    psychology
    绝恋诗词
    一万小时定律
    王国维的人生三重境界
    2017年日历
    018 cisco 3560 MAC地址绑定
    017 SSH
    016 ppp authentication
    unity Tilemap
  • 原文地址:https://www.cnblogs.com/jsydb/p/12341035.html
Copyright © 2011-2022 走看看