前言
最近做项目,会很经常接触到 offsetLeft 和 offsetTop ,用的时候还是有点迷迷糊糊,所以今天做几个demo彻底理清下这两个到底根据谁来计算距离
offsetLeft和offsetTop是什么?
首先来看MDN文档是怎么来介绍的: HTMLElement.offsetLeft 和 HTMLElement.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也就呼之欲出了。