jQuery源码解析之position()

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

内容简介: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()》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Pro JavaScript Design Patterns

Pro JavaScript Design Patterns

Dustin Diaz、Ross Harmes / Apress / 2007-12-16 / USD 44.99

As a web developer, you’ll already know that JavaScript™ is a powerful language, allowing you to add an impressive array of dynamic functionality to otherwise static web sites. But there is more power......一起来看看 《Pro JavaScript Design Patterns》 这本书的介绍吧!

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

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具