margin-left导致CSS中style.left与offsetLeft取值数值上不等的情况

吐槽君 分类:javascript

style.left & offsetLeft

left 是css样式中的一个属性,left 属性规定元素的左边缘。该属性定义了定位元素左外边距边界与其包含块左边界之间的偏移。(如果 "position" 属性的值为 "static",那么设置 "left" 属性不会产生任何效果。)具体可参阅W3C school
offsetLeft 是HTML DOM Element对象的一个属性,offsetLeft属性返回当前元素左上角相对于HTMLElement.offsetParent节点的左边界偏移的像素值。具体可参阅MDN.
当然,style.left返回的是带有"px"的 可读写字符串类型,offsetLeft返回的是只读的数字型,本文中的比较均是取style.left中的数字值。

代码示例

HTML代码:

    <body>
    <img src="images2/pikachu2.jpg" style="left: 0">
    <button>btn</button>
    </body>
 

CSS代码:

        * {
            padding: 0;
            margin: 0;
        }

        img {
            position: relative;
            margin-left: 300px;
        }
 

JS代码:

    function Animate(el, targetP) {
        let currentP = el.offsetLeft  //获取元素的当前位置
        let step = targetP > currentP ? 10 : -10  //每次移动10px
        let animateTimer = setInterval(() => {
            currentP = el.offsetLeft  //重新获取元素的当前位置
            el.style.left = currentP + step + "px"
            //当目标位置和当前位置的距离差的绝对值小于要移动步数的绝对值时,直接移动到目标位置并清除定时器
            if (Math.abs(targetP - currentP) < Math.abs(step)) {
                el.style.left = targetP + "px"
                clearInterval(animateTimer)
            }
        }, 15)
    }

    let image = document.querySelector("img")
    let btn = document.querySelector("button")
    btn.addEventListener("click", () => {
        Animate(image, 200)
    })
 

Animate(el, targetP)作用是将对应DOM元素水平移动到指定位置。
Animate(image, 200)的目的是将图片水平移动到200px的位置(由于我们在图片上设了300px的margin-left值,所以预期的效果应该是图片左移300-200=100px的距离),但实际上上面代码的运行结果是图片一直右移且永不停止。

margin-left导致CSS中style.left与offsetLeft取值数值上不等的情况_img1.png

从图片里我们可以看到,当程序运行到27行断点处时,图片对象的style.left数值上是0,其offsetLeft值是300;
margin-left导致CSS中style.left与offsetLeft取值数值上不等的情况_img2.png
在程序运行到30行断点处时,图片对象的style.left数值上是290,其offsetLeft值是590。继续执行程序我们会发现,offsetLeft值始终比style.left大300。
这就是因为图片元素有300px的margin-left值,所以元素本身左上角的在距离浏览器300px的绿色垂直线上,而元素本身的左外边距边界在浏览器左边缘的蓝色垂直线(画的很粗是为了突显那条线)上,从HTML中可以看到,图像元素的父级就是body,所以它的父亲节点/包含图像的盒子就是body本身,body的左边界在蓝色垂直线上,所以style.left=0,而offsetLeft即margin-left的值300;而在第二张图的30行断点处,经过28行后图像已经水平移动到left=290px即距离第一张图片的绿色垂直线290px的位置,此时图片左上角距离body的左边缘距离(offsetLeft)为300+290=590,所以两者值始终不相等。

代码示例修改后

那要怎样保证两者值相等呢?自然是要保证第一张图的蓝色和绿色在同一垂直线上,即图片的左外边距边界和图片的左上角位于同一垂直线上,也即去掉margin-left的值,如果实在想要margin-left,也可以在图片的外层套一个相对定位(position默认值是static,会导致style.left属性无效)的div,把margin-left属性设置在该div上,这样一来图片左外边距边界、图片元素左边界、图片元素父元素左边界均在div的左边界上,就达到了我们要的效果。

<head>
    <meta charset="UTF-8">
    <title>测试</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        div {
            position: relative;
            margin-left: 300px;
            left: 50px;
        }
        img {
            position: relative;
        }
    </style>
</head>
<body>
<div><img src="images2/pikachu2.jpg"></div>
</body>
 

js部分代码不变。

结论

本文所讨论的情况下,当前元素的margin-left属性若已设值,则style.left与offsetLeft在数值上不等,否则是相等的。

回复

我来回复
  • 暂无回复内容