资讯专栏INFORMATION COLUMN

jQuery源码解析之position()

sunsmell / 2145人阅读

摘要:注意的本质是的解析整体上看,是一个语句由于的元素,是相对于浏览器窗口进行定位的,所以它的偏移就是,即获取某个元素相对于视窗的位置。注意的本质是的完

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

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



这是divTwo

源码:

  // 返回被选元素相对于父元素(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,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.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)

Github:
https://github.com/AttackXiao...

(完)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104106.html

相关文章

  • jQuery源码解析offset()

    摘要:也就是说的本质为参数的属性减去对应的默认属性加上上的对应属性。所以的本质即相对于,对其,进行操作,而不是像那样相对于左上角原点进行操作这样就需要先减去中的的值了。 showImg(https://upload-images.jianshu.io/upload_images/5518628-d5ec366642ca36ab.jpg?imageMogr2/auto-orient/strip...

    keke 评论0 收藏0
  • jQuery源码解析$().animate()(上)

    摘要:前言需要先看源码解析之和一举例的宽度先变成,再变成,最后变成这是在异步调用中,进行同步调用动画是异步的就是连续调用二作用通过样式将元素从一个状态改变为另一个状态源码之前有说过是的方法源码行是否是空对象,方法执行单个动画的封装的本质是执行 showImg(https://segmentfault.com/img/remote/1460000019594521); 前言:需要先看 jQue...

    Batkid 评论0 收藏0
  • jQuery源码解析clone()

    摘要:五作用的关键方法,用来从目标节点克隆数据添加事件给克隆的元素注意采用数据分离的方法来保存上的事件和数据,利用标记每个元素,然后在内存上,将每个元素相关的数据放到内存中,然后在和内存的数据之间建立映射。 showImg(https://segmentfault.com/img/remote/1460000018991125); 前言:这篇讲完后,jQuery的文档处理就告一段落了,有空我...

    coolpail 评论0 收藏0
  • jQuery源码解析jQuery.event.dispatch()

    摘要:一起源方法最终是用绑定事件的而方法正是等于二作用触发绑定的事件的处理程序源码源码行即原生触发事件的处理程序修正对象获取事件的处理程序集合,结构如下从数据缓存中获取事件处理集合即目标元素委托目标这段代码压根不会执行,因为全局搜索没找到结构 showImg(https://segmentfault.com/img/remote/1460000019464031); 一、起源jQuery.e...

    GraphQuery 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<