深入理解定位父级offsetParent及偏移大小

[转载]

前面的话

  偏移量(offset dimension)是javascript中的一个重要的概念。涉及到偏移量的主要是offsetLeft、offsetTop、offsetHeight、offsetWidth这四个属性。当然,还有一个偏移参照——定位父级offsetParent。本文将详细介绍该部分内容

定位父级

  在理解偏移大小之前,首先要理解offsetParent。人们并没有把offsetParent翻译为偏移父级,而是翻译成定位父级,很大原因是offsetParent与定位有关

  定位父级offsetParent的定义是:与当前元素最近的经过定位(position不等于static)的父级元素,主要分为下列几种情况

  【1】元素自身有fixed定位,offsetParent的结果为null

  当元素自身有fixed固定定位时,我们知道固定定位的元素相对于视口进行定位,此时没有定位父级,offsetParent的结果为null

  [注意]firefox浏览器有兼容性问题

  1. <div id="test" style="position:fixed"></div>
  2. <script>
  3. //firefox并没有考虑固定定位的问题,返回<body>,其他浏览器都返回null
  4. console.log(test.offsetParent);
  5. </script>

  【2】元素自身无fixed定位,且父级元素都未经过定位,offsetParent的结果为<body>

  1. <div id="test"></div>
  2. <script>
  3. console.log(test.offsetParent);//<body>
  4. </script>

  【3】元素自身无fixed定位,且父级元素存在经过定位的元素,offsetParent的结果为离自身元素最近的经过定位的父级元素

  1. <div id="div0" style="position:absolute;">
  2. <div id="div1" style="position:absolute;">
  3. <div id='test'></div>
  4. </div>
  5. </div>
  6. <script>
  7. console.log(test.offsetParent); //<div id="div1">
  8. </script>

  【4】<body>元素的parentNode是null

  1. console.log(document.body.offsetParent);//null

IE7-浏览器Bug

  对于定位父级offsetParent来说,IE7-浏览器存在以下bug

  【bug1】当元素本身经过绝对定位相对定位,且父级元素无经过定位的元素时,IE7-浏览器下,offsetParent是<html>

  1. <div id="test" style="position:absolute;"></div>
  2. <script>
  3. //IE7-浏览器返回<html>,其他浏览器返回<body>
  4. console.log(test.offsetParent);
  5. </script>
  1. <div id="test" style="position:relative;"></div>
  2. <script>
  3. //IE7-浏览器返回<html>,其他浏览器返回<body>
  4. console.log(test.offsetParent);
  5. </script>
  1. <div id="test" style="position:fixed;"></div>
  2. <script>
  3. //firefox并没有考虑固定定位的问题,返回<body>,其他浏览器都返回null
  4. console.log(test.offsetParent);
  5. </script>

  【bug2】如果父级元素存在触发haslayout的元素或经过定位的元素,且offsetParent的结果为离自身元素最近的经过定位或触发haslayout的父级元素

  [注意]关于haslayout的详细信息移步至此

  1. <div id="div0" style="display:inline-block;">
  2. <div id='test'></div>
  3. </div>
  4. <script>
  5. //IE7-浏览器返回<div id="div0">,其他浏览器返回<body>
  6. console.log(test.offsetParent);
  7. </script>
  1. <div id="div0" style="position:absolute;">
  2. <div id="div1" style="display:inline-block;">
  3. <div id='test'></div>
  4. </div>
  5. </div>
  6. <script>
  7. //IE7-浏览器返回<div id="div1">,其他浏览器返回<div id="div0">
  8. console.log(test.offsetParent);
  9. </script>
  1. <div id="div0" style="display:inline-block;">
  2. <div id="div1" style="position:absolute;">
  3. <div id='test'></div>
  4. </div>
  5. </div>
  6. <script>
  7. //所有浏览器都返回<div id="div1">
  8. console.log(test.offsetParent);
  9. </script>

偏移量

  偏移量共包括offsetHeight、offsetWidth、offsetLeft、offsetTop这四个属性

offsetWidth

  offsetWidth表示元素在水平方向上占用的空间大小,无单位(以像素px计)

  1. offsetWidth = border-left-width + padding-left + width + padding-right + border-right-width;

offsetHeight

  offsetHeight表示元素在垂直方向上占用的空间大小,无单位(以像素px计)

  1. offsetHeight = border-top-width + padding-top + height + padding-bottom + border-bottom-width
  1. <div id="test" style="width:100px; height:100px; padding:10px; margin:10px; border:1px solid black;"></div>
  2. <script>
  3. //122=1+10+100+10+1
  4. console.log(test.offsetWidth);
  5. console.log(test.offsetHeight);
  6. </script>

  [注意]如果存在垂直滚动条,offsetWidth也包括垂直滚动条的宽度;如果存在水平滚动条,offsetHeight也包括水平滚动条的高度

  1. <div id="test" style="width:100px; height:100px; padding:10px; margin:10px; border:1px solid black; overflow: scroll;"></div>
  2. <script>
  3. //IE8-浏览器将垂直滚动条的宽度计算在width宽度和height高度中,width和height的值仍然是100px;
  4. //而其他浏览器则把垂直滚动条的宽度从width宽度中移出,把水平滚动条的高度从height高度中移出,则滚动条宽度为17px,width宽度和height高度为剩下的83px
  5.  
  6. if(window.getComputedStyle){
  7. console.log(getComputedStyle(test).width,getComputedStyle(test).height)//83px
  8. }else{
  9. console.log(test.currentStyle.width,test.currentStyle.height);//100px
  10. }
  11. //122=1+10+100+10+1
  12. console.log(test.offsetWidth,test.offsetHeight);
  13. </script>

offsetTop

  offsetTop表示元素的上外边框至offsetParent元素的上内边框之间的像素距离

offsetLeft

  offsetLeft表示元素的左外边框至offsetParent元素的左内边框之间的像素距离

  1. <div id="out" style="padding: 5px;position: relative;margin: 6px;border:1px solid black">
  2. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  3. </div>
  4. <script>
  5. //15=test.marginTop(10) + out.paddingTop(5)
  6. alert(test.offsetTop);
  7. //15=test.marginLeft(10) + out.paddingLeft(5)
  8. alert(test.offsetLeft);
  9. </script>

IE7-Bug

  IE7-浏览器在offsetTop属性的处理上存在bug

  【1】若父级设置position: relative,则在IE7-浏览器下,offsetTop值为offsetParent元素的paddingBottom值

  1. <div id="out" style="padding: 5px;position: relative;">
  2. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  3. </div>
  4. <script>
  5. //其他浏览器返回15(5+10),而IE7-浏览器返回5
  6. console.log(test.offsetTop);
  7. </script>

  【2】若父级设置position: aboslute(或其他触发haslayout的条件),offsetTop值为offsetParent元素的paddingBottom值和当前元素的marginTop值的较大值

  1. <div id="out" style="padding: 5px;position:absolute;">
  2. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  3. </div>
  4. <script>
  5. //其他浏览器返回15(5+10),而IE7-浏览器返回10(10和5的较大值)
  6. console.log(test.offsetTop);
  7. </script>

页面偏移

  要知道某个元素在页面上的偏移量,将这个元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,并加上offsetParent的相应方向的边框,如此循环直到根元素,就可以得到元素到页面的偏移量

  [注意]在默认情况下,IE8-浏览器下如果使用currentStyle()方法获取<html>和<body>(甚至普通div元素)的边框宽度都是medium,而如果使用clientLeft(或clientTop)获取边框宽度,则是实际的数值

  1. html,body{border: 0;}
  2. body{margin:0;}
  1. function getElementLeft(element){
  2. var actualLeft = element.offsetLeft;
  3. var current = element.offsetParent;
  4. while(current != null){
  5. actualLeft += current.offsetLeft + current.clientLeft;
  6. current = current.offsetParent;
  7. }
  8. return actualLeft + 'px';
  9. }
  10. function getElementTop(element){
  11. var actualTop = element.offsetTop;
  12. var current = element.offsetParent;
  13. while(current != null){
  14. actualTop += current.offsetTop + current.clientTop;
  15. current = current.offsetParent;
  16. }
  17. return actualTop + 'px';
  18. }
  1. <div style="padding: 20px;border:1px solid black;position:absolute;">
  2. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  3. </div>
  4. <script>
  5. //其他浏览器返回31(10+20+1),而IE7-浏览器返回21((20和10的较大值)+1)
  6. console.log(getElementTop(test));
  7. //所有浏览器返回31(10+20+1)
  8. console.log(getElementLeft(test));
  9. </script>

注意事项

  【1】所有偏移量属性都是只读的

  1. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  2. <script>
  3. console.log(test.offsetWidth);//100
  4. //IE8-浏览器会报错,其他浏览器则静默失败
  5. test.offsetWidth = 10;
  6. console.log(test.offsetWidth);//100
  7. </script>

  【2】如果给元素设置了display:none,则它的偏移量属性都为0

  1. <div id="test" style="width:100px; height:100px; margin:10px;display:none"></div>
  2. <script>
  3. console.log(test.offsetWidth);//0
  4. console.log(test.offsetTop);//0
  5. </script>

  【3】每次访问偏移量属性都需要重新计算

  1. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  2. <script>
  3. console.time("time");
  4. for(var i = 0; i < 100000; i++){
  5. var a = test.offsetWidth;
  6. }
  7. console.timeEnd('time');//65.129ms
  8. </script>
  1. <div id="test" style="width:100px; height:100px; margin:10px;"></div>
  2. <script>
  3. console.time("time");
  4. var a = test.offsetWidth;
  5. for(var i = 0; i < 100000; i++){
  6. var b = a;
  7. }
  8. console.timeEnd('time');//1.428ms
  9. </script>

  由上面代码对比可知,重复访问偏移量属性需要耗费大量的性能,所以要尽量避免重复访问这些属性。如果需要重复访问,则把它们的值保存在变量中,以提高性能

好的代码像粥一样,都是用时间熬出来的

深入理解定位父级offsetParent及偏移大小offsetTop / offsetLeft / offsetHeight / offsetWidth的更多相关文章

  1. 深入理解定位父级offsetParent及偏移大小

    前面的话 偏移量(offset dimension)是javascript中的一个重要的概念.涉及到偏移量的主要是offsetLeft.offsetTop.offsetHeight.offsetWid ...

  2. javascript好文---深入理解定位父级offsetParent及偏移大小

    前面的话 偏移量(offset dimension)是javascript中的一个重要的概念.涉及到偏移量的主要是offsetLeft.offsetTop.offsetHeight.offsetWid ...

  3. [转]深入理解定位父级offsetParent及偏移大小

    偏移量(offset dimension)是javascript中的一个重要的概念.涉及到偏移量的主要是offsetLeft.offsetTop.offsetHeight.offsetWidth这四个 ...

  4. CSS定位 深入理解定位(position)的偏移

    前言 CSS有三种基本的布局机制:普通流.浮动和绝对定位.利用定位,可以准确地定义元素框相对于其正常位置应该出现的位置,或者相对于父元素.另一个元素甚至浏览器窗口本身的位置.但元素究竟如何定位,定位到 ...

  5. [jQuery]相对父级元素的fixed定位

    (function($) {     var DNG = {};     //----------------------------------------------------/     // ...

  6. width:100%与绝对定位同时存在,偏移出父级容器

    当父级容器内的子元素width设为100%,而子元素又有绝对定位时,子元素伸展超出父级容器,像下面 出现这种情况的原因,width:100%,这个百分之百是相对其定位父级而言的,其定位父级有多宽,这个 ...

  7. JS-offsetParent定位父节点

    offsetParent:离当前元素最激动呢一个有定位的父节点 如果没有定位父级,默认是body IE7以下如果当前元素没有定位默认是body,如果有定位就是html IE7以下,如果当前元素的某个父 ...

  8. css使absolute相对于父容器进行定位而不是以body(为什么绝对定位(absolute)的父级元素必须是相对定位(relative))

    借知乎的回答如下解释: 首先,我想告诉你的是,如果父级元素是绝对定位(absolute)或者没有设置,里面的绝对定位(absolute)自动以body定位.这句话是错的.正确的是:只要父级元素设了po ...

  9. html css样式子元素相对父级元素定位

    废话不多说. 父级元素 样式设置: position:relative; 子元素样式: position: absolute; 这样就可以达到子元素相对父级元素定位了.

随机推荐

  1. delphi 字符串string转流TStream

    function StringToFile(mString : string; mFileName : TFileName) : Boolean;var vFileChar : file of Cha ...

  2. SQLServer数据库镜像配置

    目录 一.目标...2 二.前提条件.限制和建议...2 三.设置概述...2 四.安装Sql Server 2008 enterprise X64.3 4.1.安装.NET3.5.3 4.2.安装时 ...

  3. The Weather

    后台: <?php //接受查询的城市 $city = $_GET['city']; //连接redis $redis = new redis(); $redis->connect(&qu ...

  4. java反编译工具cfr

    http://www.benf.org/other/cfr/ 下载反编译工具 @echo off SET home=%cd% rem 指定存放jar包的目录 SET jar_dir=%home%\ja ...

  5. 网络编程 tftp下载文件的编程

    一.代码 操作码 功能 1 读请求,即下载 2 写请求,即上传 3 表示数据包,即DATA 4 确认码,即ACK 5 错误 from socket import * import struct s=s ...

  6. 李清华201772020113《面向对象程序设计(Java)》第六周学习总结

    第一部分 理论知识 1.继承 用已有类来构建新类的一种机制.新类可以继承父类的方法和域,同时可以在新类中添加新的方法和域. 已有类称为:超类.基类或父类.新类称作:子类.派生类或孩子类. 子类的构造器 ...

  7. 转 cxgrid属性说明

    TCXGRID控件:属性:ActiveLevel: 当前层BorderStyle: 窗口风格Color: 颜色FocusedView: 当前View;Font: 字体LevelTabs: 类似Page ...

  8. RTTI和反射小结

    Java有两种方式让我们在运行时识别对象和类的信息:1.“传统的”RTTI,假定所有的类型编译时已知:2.“反射”机制,允许在运行时发现和使用类的信息. 一.RTTI RTTI(Run-Time Ty ...

  9. java应用健康检查

    本文主要针对自己手写shell监控应用状态,有可系统解决方案的,比如K8S,可以略过 #!/bin/sh#health_check.sh count=`ps -ef | grep test.jar | ...

  10. JVM系列2:垃圾收集器与内存分配策略

    垃圾收集是一个很大话题,本文也只是看了深入理解Java虚拟机总结了下垃圾收集的知识. 首先按照惯例,先上思维导图: 垃圾收集简而言之就是JVM帮我们清理掉内存区域不需要的数据.它主要负责清理堆中实例对 ...