offsetLeft深入研究

基础示例代码-祖先元素无定位元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>offsetLeft研究</title>
</head>
<body style="border: 20px solid #000;">
    <div id="box1" style="border: 20px solid #666; width:300px; height:300px; padding-left:20px; margin-left:20px;">
        <div id="box2" style="border: 20px solid #999; width:200px; height: 200px; padding-left:20px; margin-left:20px;">
            <div id="box3" style="border: 20px solid [[bbb]];width:100px; height: 100px; padding-left:20px; margin-left: 20px; overflow:scroll; white-space: nowrap;">
                <div id="box4">这里是内容区域,父级为box3,父级超出部分滚动</div>
            </div>
        </div>
    </div>
</body>
<script>
    function clickFun(e){
        console.log('鼠标点击点的ClientX:', e.clientX);
        console.log('鼠标点击点的layerX:', e.layerX);
        console.log('鼠标点击点的pageX:', e.pageX);
        console.log('鼠标点击点的offsetX:', e.offsetX);
        console.log('鼠标点击点的screenX:', e.screenX);
        console.log('鼠标点击点的x:', e.x);
        console.log('box1的offsetLeft', $('box1').offsetLeft);
        console.log('box2的offsetLeft', $('box2').offsetLeft);
        console.log('box3的offsetLeft', $('box3').offsetLeft);
        console.log('box4的offsetLeft', $('box4').offsetLeft);
    }

    function $(id){
        return document.getElementById(id);
    }

    function addListener(type, fun) {
        if (typeof window.addEventListener != "undefined") {
                window.addEventListener(type, fun, false);
        } else {
            window.attachEvent('on' + type, fun)
        }    
    }
    window.onload = function(){
        addListener('click', clickFun);
    };
        
    
</script>
</html>

我们设计了实验代码结构,由body中的四层嵌套盒子组成,除最里层的box4外每个盒子都有20像素宽度的边框,20像素的左内边距,和20像素的左外边距。

当鼠标点击页面时,控制台输出当前点击位置的一些位置数据(只研究横向的,所以只打印横坐标值),同时打印输入四层盒子的offsetLeft的值。

同时,box3设计了内容宽度超出容器后显示滚动条,以便验证滚动条对offsetLeft的影响。

先看默认状态下点击box4的左侧边框位置后输出的结果:

将截图放大观察,得出我们的

结论1 :在祖先元素中没有定位元素时,元素的offset值为其左边框的最左侧位置到html页面左边框的距离值

一点引申

根据输出结果,我们还可以猜想以下两个值的意义:

offsetX: 根据值的大小猜测为当前位置距离当前点击对象(e.target,这里为box4)左边框的值,至于是左边框的左侧还是右侧,需要进一步验证;

layerX: 当前点击位置距离当前点击元素(e.target,这里为box4)父级元素左边框左侧的距离值

我们验证一下,这次点击box2 左边框和box3左边框中间的位置:

可以看到,此时的offset值为20,正好是当前点击对象(box2)的左边框右侧位置到当前点击位置的距离。

而此时的layerX值为148,并不是当前点击位置距离当前点击对象父级元素box1的左边框左侧的距离值,与我们的猜想不符,我们看看MDN的说明:

The **UIEvent.layerX** read-only property returns the horizontal coordinate of the event relative to the current layer.

UIEvent.layerX只读属性返回事件相对于当前层的水平坐标。

This property takes scrolling of the page into account and returns a value relative to the whole of the document unless the event occurs inside a positioned element, where the returned value is relative to the top left of the positioned element.

此属性将页面滚动考虑在内,并返回一个相对于整个文档的值,除非事件发生在定位元素内,其中返回值相对于定位元素的左上角

按照该说明,这个属性返回事件相对于当前层的坐标,那么很明显,当前层并不是事件目标元素的父元素,对于层的概念,大概和盒子的渲染模型有关,没有深入研究过,鉴于这个属性并不是W3C标准属性,各个浏览器实现也没有统一标准,不再深究。

滚动的影响

为了验证滚动是否对offsetLeft的值,我们拖动box3的滚动条,然后同样点击box4的左边框位置,看下结果:

可以看到,滚动条滚动后,box4在页面上的绝对位置时改变了的,但是它(以及祖先元素)的offsetLeft属性并没有变化,得到

结论2: 滚动区域内的内容滚动后,其offsetLeft不受影响。

body元素定位

因为offsetLeft是相对于其offsetParent属性指向的元素(离它最新的定位祖先元素)的偏移量,所以必须研究当它的offsetParent发生改变时它的值的变化。首先,为body添加相对定位。

<body style="border: 20px solid #000; position: relative;">

使用变量控制实验法,仍旧点击box4的左边框,看看控制台输出:

image-20200807185114782

这次比我们第一次点击,位置时相同的(clientX都是208),但是所有元素的offsetLeft的值都少了8px,而这正好是我们第一张图中标识的body元素左边框左侧到html边框的距离。

这种变化是因为一开始box4的祖先元素中没有定位元素,所以其默认定位元素时body,但此时的offsetLeft包含了bodymargin-left值(在chrome中默认为8px),而此时body被明确定义为了定位元素,此时的offsetLeft则不再包含bodymargin-left值。

我们在代码中把bodymargin值去掉,来验证下以上说法:

<body style="border: 20px solid #000; position: relative;margin:0;">

然后再看:

image-20200807190149050

这里没有了margin值,所以我们修正一下

结论1:当元素的祖先元素中没有定位元素时,offsetLeft 就是元素相对于其offsetParent所指向的元素(body)的左边框的左侧到元素左边框左侧的距离;

现在就有疑问了,根据offsetParent的特征,第一张图那里offsetParent明显也是body元素,为什么它定位和不定位会有包含不包含margin值的区别呢?

body取消定位,box1定位

<body style="border: 20px solid #000; margin:0;">
    <div id="box1" style="border: 20px solid #666; width:300px; height:300px; padding-left:20px; margin-left:20px; position: relative;">

我们取消body元素的定位,给box1定位,此时,根据offsetParent的规则,box1/box2/box3offsetParent应该是box1,我们点击相同位置,看输出结果:

image-20200807191233454

此时,这些值都发生了变化:

  • box1当前的offsetParent还是body元素,所以其offsetLeft还是它的左边框左侧到body左边框左侧的距离(40),这个距离包含它自己的margin-left值。
  • box2offsetParentbox1,所以它的offsetLeft值是它的左边框左侧到box1左边框左侧的距离(60)减去它自己的margin-left值(20)之后的值(40)。
  • box3offsetParentbox1,所以它的offsetLeft值是它的左边框左侧到box1左边框左侧的距离(120)减去它自己的margin-left值(20)之后的值(100)。
  • box4offsetParentbox1,所以它的offsetLeft 是它的左边框左侧到 box1 左边框左侧的距离 (160)减去 它自己的margin-left值(20)之后的值(140)。

我们得到了

结论3:当祖先元素中有定位元素时,元素的offsetLeft的值等于它的左边框左侧到它的offsetParent元素左边框的距离减去它自身的margin-left值。

元素自身为fixed定位

由于元素自身为fixed定位时,它的offsetParent总是为null,那么作为一个基于offsetParent的偏移量,元素的offsetLeft的结果又将是什么呢?

我们修改代码,在上一个例子的基础上,将box2的定位设为fiexd

<body style="border: 20px solid #000; margin:0;">
    <div id="box1" style="border: 20px solid #666; width:300px; height:300px; padding-left:20px; margin-left:20px; position: relative;">
        <div id="box2" style="border: 20px solid #999; width:200px; height: 200px; padding-left:20px; margin-left:20px; position:fixed">

再次实验:

image-20200810102410521

这次的结果变化比较有意思,我们逐一分析:

  • 此时 box1offsetParent仍旧是 body元素,所以其offsetLeft还是它的左边框左侧到body左边框左侧的距离(40),这个距离包含它自己的margin-left值。

  • box2由于其自身是fiexd 定位,我们可以看到它的offsetParentnull,可是它的offsetLeft却并不为null 而是其左边框左侧到body左边框左侧的距离,这个距离包含它自己的margin-left值。

  • box3offsetParent此时变成离它最近的定位祖先元素box2,它的offsetLeft值则是它自己的左边框左侧到

    box2的左边框左侧的距离,减去它自己的margin-left值。

  • box4offsetParent 此时也变成了 box2,它的offsetLeft值是它自己的左边框左侧到box2的左边框左侧的距离,减去它自己的margin-left值。

我们得到

**结论4:当元素自身为fixed定位时,它的offsetLeft值等于它自己的左边框左侧到body左边框左侧的距离,这个距离包含其自身的margin-left值 **

元素自身隐藏

根据offsetParent的规则,当元素自身的displaynone时, 它的offsetParntnull,那么它的offsetLeft会不会发生变化呢?

我们在上一个例子的基础上将box3display设置为none:

<body style="border: 20px solid #000; margin:0;">
    <div id="box1" style="border: 20px solid #666; width:300px; height:300px; padding-left:20px; margin-left:20px; position: relative;">
        <div id="box2" style="border: 20px solid #999; width:200px; height: 200px; padding-left:20px; margin-left:20px; position: relative;">
            <div id="box3" style="border: 20px solid [[bbb]];width:100px; height: 100px; padding-left:20px; margin-left: 20px; overflow:scroll; white-space: nowrap; display: none;">

此时由于box3 设置为了隐藏,所以box3box4在页面上不可见了,我们无法再点击box4来进行试验,但是其实根据我们上面的试验,也同时发现,无论鼠标点击哪里,元素的offsetLeft值不会随着鼠标点击位置的不同而变化,所以我们就随意点击页面了,看下结果:

观察后发现:

  • box1box2offsetLeft仍旧遵循之前的规则
  • box3box4offsetParentnull ,而他们的offsetLeft为0

我们得到

**结论5:当元素自身displaynone时,它的offsetLeft值为0 **

至此,关于offsetLeft,我们得出了以下结论:

  • 当元素的祖先元素中没有定位元素时,它的offsetLeft值等于它自己的左边框左侧到body左边框左侧的距离,这个距离包含其自身的margin-left值。
  • 当祖先元素中有定位元素时,元素的offsetLeft的值等于它的左边框左侧到它的offsetParent元素左边框的距离减去它自身的margin-left值。
  • 当元素自身为fixed定位时,它的offsetLeft值等于它自己的左边框左侧到body左边框左侧的距离,这个距离包含其自身的margin-left值。
  • 当元素自身displaynone时,它的offsetLeft值为0
  • 滚动区域内的内容滚动后,内容所在元素的offsetLeft值不受影响。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值