几个Demo带你理清offsetLeft和offsetTop

1,328 阅读4分钟

前言

最近做项目,会很经常接触到 offsetLeftoffsetTop ,用的时候还是有点迷迷糊糊,所以今天做几个demo彻底理清下这两个到底根据谁来计算距离

offsetLeft和offsetTop是什么?

首先来看MDN文档是怎么来介绍的: HTMLElement.offsetLeftHTMLElement.offsetTop 是一个只读属性,返回当前元素左上角相对于 HTMLElement.offsetParent 节点的左内边界偏移的像素值。 这里又牵扯到一个新概念: offsetParent,这又是什么呢?

offsetParent又是什么呢?

MDN文档搜索得知 HTMLElement.offsetParent 是一个只读属性,返回一个指向最近的(指包含层级上的最近)包含该元素的定位元素或者最近的 table,td,th,body元素。当元素的 style.display 设置为 "none" 时,offsetParent 返回 null。offsetParent 很有用,因为 offsetTop 和 offsetLeft都是相对于其内边距边界的。

具体demo分析

对以下元素结构进行分析

<div class="wrap">红色
    <div class="wrap2">黄色
        <div class="inner">粉色</div>
    </div>
</div>

对offsetLeft和offsetTop进行输出打印

var inner=document.querySelector('.inner')
console.log('offsetLeft:'+inner.offsetLeft,'offsetTop:'+inner.offsetTop)

页面效果如下:

1).本身定位不为fixed,父级有定位

.wrap{position:relative;left: 20px; background-color: red;}
.wrap2{position:relative;background-color: yellow;}
.inner{background-color: pink;}

输出如下:

以上可以得出什么呢?粉色框因为没开定位,所以他的offsetTop是根据最近的开启定位的父元素的内边框来计算距离(字体占了21)。

.wrap{position:relative;left: 20px; background-color: red;}
.wrap2{position:relative;background-color: yellow;}
.inner{position:absolute;background-color: pink;}

输出为:

这里粉色框开了定位且不为fiexd,但是他的offsetTop还是根据最近的开启定位的父元素的内边框来计算距离(字体占了21)。

所以我们总结为只要自身不为fiexd定位,offsetTop和offsetLeft就根据最近的开启定位的父元素的内边框来计算距离,这是因为offsetParent在这种情况下指向的都是最近的开启定位的父元素,这也说明了offsetTop和offsetLeft确实是根据offsetParent来计算的

2).本身定位不为fixed,父级没有定位

.wrap{margin: 10px 20px; background-color: red;}
.wrap2{background-color: yellow;}
.inner{background-color: pink;}//自身没开启定位

输出为:

.wrap{margin: 10px 20px; background-color: red;}
.wrap2{background-color: yellow;}
.inner{position:relative;background-color: pink;}//自身开启非fixed的定位

输出为:

我们可以看到这里的父元素没有开启定位元素,那这里的offsetTop和offsetLeft是根据什么呢?答案是body.让我们来算下offsetLeft如果按父元素应该是0,这里是20,其实就是因为加了20的margin-left.offsetTop如果按父元素应该是21或者42,这里是52,其实就是因为最外面的父元素加了10的margin-left.就等于粉色框距离body的长度。所以总结为只要自身不为fiexd定位,offsetTop和offsetLeft就根据最近的开启定位的父元素的内边框来计算距离,如果父元素都没有开启定位,那就按照body作为标准来计算。

3).本身定位为fixed,浏览器不为火狐(这边只给出在chrome下的结果,其他类似

.wrap{margin: 10px 20px; background-color: red;}
.wrap2{background-color: yellow;}
.inner{position:fixed;background-color: pink;}

chrome输出为:

.wrap{margin: 10px 20px; background-color: red;}
.wrap2{position:relative;background-color: yellow;}
.inner{position:fixed;background-color: pink;}

chrome输出依然为: 我们可以得到,当自身定位为fixed,且浏览器不为火狐,不管父元素有没有开启定位,他的offsetParent都是指向null,但是他的offsetTop和offsetLeft依然是根据body来计算的

4).本身定位为fixed,浏览器是火狐

.wrap{margin: 10px 20px; background-color: red;}
.wrap2{position:relative;background-color: yellow;}
.inner{position:fixed;background-color: pink;}

火狐下输出为: 我们可以得到,当自身定位为fixed,且浏览器是火狐时,不管父元素有没有开启定位,他的offsetParent都是指向body,所以他的offsetTop和offsetLeft自然是根据body来计算的

总结

从上面多个例子我们可以知道offsetLeft和offsetTop确实是根据offsetParent来计算的,所以我们需要知道offsetParent在某个情况具体指的是啥,因为他需要考虑兼容性还有父子元素是否开启定位等复杂情况,测试情况过于冗余,下面就直接给出本文结论:

  • 本身定位为fixed
    • offsetParent:null(不是火狐),此时offsetLeft和offsetTop是根据body来计算的
    • offsetParent:body(火狐)
  • 本身定位不为fixed
    • offsetParent: body(祖先元素没有开启定位)
    • offsetParent:定位父级(祖先元素开启了定位) 知道了offsetParent,对应的offsetLeft和offsetTop也就呼之欲出了。