jQuery源码解析之position()

栏目: jQuery · 发布时间: 5年前

内容简介:position()作用:

jQuery源码解析之position()

position()

作用:

返回被选元素相对于 父元素(parent) 的偏移坐标

使用:

直接调用 $().position() 即可,该方法没有 arguments(参数对象)

<body>
<script src="jQuery.js"></script>
<p id="pTwo">这是divTwo</p>
<script>
  $("#pTwo").position() //{top: 0, left: 8}
</script>
</body>

源码:

// 返回被选元素相对于父元素(parent)的偏移坐标
    // 可以理解成被选元素设置为absolute,
    // 然后设置left、top的值就是相对于父元素的偏移坐标
    // 源码10571行
    // position() relates an element's margin box to its offset parent's padding box
    // This corresponds to the behavior of CSS absolute positioning
    position: function() {
      // 如果DOM元素不存在,直接返回
      if ( !this[ 0 ] ) {
        return;
      }

      var offsetParent, offset, doc,
        elem = this[ 0 ],
        parentOffset = { top: 0, left: 0 };

      // position:fixed elements are offset from the viewport, which itself always has zero offset
      // position:fixed的元素,是相对于浏览器窗口进行定位的,
      // 所以它的偏移就是getBoundingClientRect(),即获取某个元素相对于视窗的位置
      if ( jQuery.css( elem, "position" ) === "fixed" ) {

        // Assume position:fixed implies availability of getBoundingClientRect
        offset = elem.getBoundingClientRect();

      }
      // 除去position是fixed的情况
      else {
        // 获取被选元素相对于文档(document)的偏移坐标
        offset = this.offset();

        // Account for the *real* offset parent, which can be the document or its root element
        // when a statically positioned element is identified
        doc = elem.ownerDocument;
        //定位目标元素的父元素(position不为static的元素)
        offsetParent = elem.offsetParent || doc.documentElement;
        // 如果父元素是<body>/<html>的话,将父元素重新定位为它们的父元素
        // body的父元素是html,html的父元素是document
        while ( offsetParent &&
        ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
        jQuery.css( offsetParent, "position" ) === "static" ) {

          offsetParent = offsetParent.parentNode;
        }
        // 如果定位父元素存在,并且不等于目标元素,并且定位元素类型是 "元素类型"
        if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {

          // Incorporate borders into its offset, since they are outside its content origin
          parentOffset = jQuery( offsetParent ).offset();
          // 这两行代码的意思是父元素的offset()要从padding算起,不包括border
          // 所以需要去掉border
          // jQuery.css( element, "borderTopWidth", true )的 true 表示返回数字,而不带单位 px
          parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
          parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
        }
      }

      // Subtract parent offsets and element margins
      // 可以看出,$().position()的本质是目标元素的offset()减去父元素的offset(),同时还要算上目标元素的margin,因为盒子模型(关键)。
      //(注意:offset()的本质是getBoundingClientRect()的top、left + pageYOffset、pageXOffset)
      return {
        top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
        left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
      };
    },

解析:

整体上看,是一个 if(...fixed) { } esle { } 语句

(1) if ( jQuery.css( elem, "position" ) === "fixed" )

if ( jQuery.css( elem, "position" ) === "fixed" ) {
        // Assume position:fixed implies availability of getBoundingClientRect
        offset = elem.getBoundingClientRect();
}
return {
        top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
        left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
      };

由于 position:fixed 的元素,是相对于浏览器窗口进行定位的,所以它的偏移就是 getBoundingClientRect() ,即获取某个元素相对于视窗的位置。

注意:

① getBoundingClientRect() 计算的是目标元素的border的位置(左上角),是不包括margin的

② 如果不加上margin的话(代码是通过 减去 ,来算上margin的),是不准确的,看下图

jQuery源码解析之position()

所以源码最后会:

- jQuery.css( elem, "marginTop", true )
- jQuery.css( elem, "marginLeft", true )

(2) jQuery.css( elem, "width", true )
true 的作用是返回该属性的数字,而不带单位 px

(3)定位父元素存在,并且不等于目标元素,并且定位元素类型是 "元素类型"的话

if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 )

是要减去 border 属性的值的

parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );

为啥?举个例子:

let p=document.querySelector("#pTwo")
console.log(p.getBoundingClientRect(),'pTwo11'); //x:8,y:16

设置 borderLeftWidth 为 8 像素

let p=document.querySelector("#pTwo")
p.style.borderLeftWidth='8px'
console.log(p.getBoundingClientRect(),'pTwo11'); //x:8,y:16

可以看到 getBoundingClientRect() 指定坐标是到 border 上的,这是不准确的,因为在里面的子元素的位置也会受父元素的 border 影响,所以父元素的坐标需要越过 border

综上:

可以看出, $().position() 的本质是目标元素的 offset() 减去父元素的 offset(),同时还要算上目标元素的 margin,算上父元素的border。

(注意:offset()的本质是getBoundingClientRect()的top、left + pageYOffset、pageXOffset)

jQuery源码解析之position()

(完)


以上所述就是小编给大家介绍的《jQuery源码解析之position()》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

我的第一本算法书

我的第一本算法书

[日]石田保辉、[日]宮崎修一 / 张贝 / 人民邮电出版社 / 2018-10 / 69.00元

本书采用大量图片,通过详细的分步讲解,以直观、易懂的方式展现了7个数据结构和26个基础算法的基本原理。第1章介绍了链表、数组、栈等7个数据结构;从第2章到第7章,分别介绍了和排序、查找、图论、安全、聚类等相关的26个基础算法,内容涉及冒泡排序、二分查找、广度优先搜索、哈希函数、迪菲 - 赫尔曼密钥交换、k-means 算法等。 本书没有枯燥的理论和复杂的公式,而是通过大量的步骤图帮助读者加深......一起来看看 《我的第一本算法书》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具