jQuery源码解析之offset()
一、offset()
作用:
返回被选元素相对于文档(document)的偏移坐标
二、三种情况使用:
1、$()
.offset()
<body> <script src="jQuery.js"></script> <p id="pTwo">这是divTwo</p> <script> $("#pTwo").offset() //{top: 16, left: 8} </script> </body>
源码:
//返回目标元素相对于doucument的偏移坐标, //即距离浏览器左上角的距离 // 返回偏移坐标:$(selector).offset() // 设置偏移坐标:$(selector).offset({top:value,left:value}) // 使用函数设置偏移坐标:$(selector).offset(function(index,currentoffset)) // offset() relates an element's border box to the document origin //源码10500行 //options即参数 //arguments是参数对象 offset: function( options ) { // Preserve chaining for setter //如果是有参数的,参数是undefined则返回目标元素本身, //否则为每个目标元素设置options console.log(options,arguments,'arguments10476') //$().offset()不走这里 if ( arguments.length ) { console.log('aaa','vvv10507') return options === undefined ? this : this.each( function( i ) { //为每个目标元素设置options jQuery.offset.setOffset( this, options, i ); } ); } var rect, win, //获取DOM节点 elem = this[ 0 ]; if ( !elem ) { return; } // jQuery不支持获取隐藏元素的偏移坐标。 // 同理,也无法取得隐藏元素的 border, margin, 或 padding 信息 //所以如果元素是隐藏的,默认返回0值 // Return zeros for disconnected and hidden (display: none) elements (gh-2310) // Support: IE <=11 only // Running getBoundingClientRect on a // disconnected node in IE throws an error //对IE的特殊处理 if ( !elem.getClientRects().length ) { return { top: 0, left: 0 }; } // Get document-relative position by adding viewport scroll to viewport-relative gBCR //返回元素的大小及其相对于视口的位置 //https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect rect = elem.getBoundingClientRect(); //返回所属文档的默认窗口对象(只读) //原点坐标 win = elem.ownerDocument.defaultView; //pageXOffset,pageYOffset 相当于 scrollX 和 scrollY //返回文档在窗口左上角水平 x 和垂直 y 方向滚动的像素 return { //16 0 // top: rect.top + win.pageYOffset, //8 0 left: rect.left + win.pageXOffset }; },
解析:
由于$()
.offset()没有参数,所以源码里的两个 if
可以忽略,所以offset()
的本质即:
let p = document.getElementById("pTwo"); let rect=p.getBoundingClientRect() //返回所属文档的默认窗口对象(只读) //原点坐标 let win = p.ownerDocument.defaultView; let offsetObj={ top: rect.top + win.pageYOffset, left: rect.left + win.pageXOffset } console.log(offsetObj,'win18')
(1)getBoundingClientRect()
该方法用于获取某个元素相对于视窗的位置集合,并返回一个对象,该对象中有top, right, bottom, left等属性,简单点就是相对于原坐标(默认是左上角)的偏移量
(2)window.pageXOffset、window.pageYOffset
返回文档在窗口左上角水平 x 和垂直 y 方向滚动的像素,相当于 scrollX 和 scrollY,简单点就是滚动的偏移量
所以offset()
的本质即:
相对于原坐标的偏移量+滚动的偏移量的总和。
2、$()
.offset({top:15,left:15})
$("#pTwo").offset({top:15,left:15})
源码:
当有参数的时候,就会走 if
中,通过jQuery.offset.setOffset( )
来处理:
if ( arguments.length ) { return options === undefined ? this : this.each( function( i ) { //为每个目标元素设置options jQuery.offset.setOffset( this, options, i ); } ); }
- jQuery.offset.setOffset( )
//offset()的关键方法 //源码10403行 jQuery.offset = { setOffset: function( elem, options, i ) { var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, //获取元素的position属性的值 //static position = jQuery.css( elem, "position" ), //过滤成标准jQuery对象 curElem = jQuery( elem ), props = {}; // Set position first, in-case top/left are set even on static elem //指定相对定位relative if ( position === "static" ) { elem.style.position = "relative"; } //{left:8,top:16} curOffset = curElem.offset(); //0px curCSSTop = jQuery.css( elem, "top" ); //0px curCSSLeft = jQuery.css( elem, "left" ); // 如果定位position是(绝对定位absolute或固定定位fixed), // 并且top,left属性包含auto的话 //false calculatePosition = ( position === "absolute" || position === "fixed" ) && ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; // Need to be able to calculate position if either // top or left is auto and position is either absolute or fixed if ( calculatePosition ) { curPosition = curElem.position(); curTop = curPosition.top; curLeft = curPosition.left; } else { //0 0 curTop = parseFloat( curCSSTop ) || 0; curLeft = parseFloat( curCSSLeft ) || 0; } //如果传的参数是function{}的话 if ( isFunction( options ) ) { // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); } //如果传的参数的有top值 if ( options.top != null ) { //参数 top - offset().top + element.top props.top = ( options.top - curOffset.top ) + curTop; } //如果传的参数的有left值 if ( options.left != null ) { //参数 left - offset().left + element.left props.left = ( options.left - curOffset.left ) + curLeft; } //自己没见过使用 using 的情况。。 if ( "using" in options ) { options.using.call( elem, props ); } //所以一般走这里,为当前元素设置top,left属性 else { //position:relative //top:xxx //left:xxx curElem.css( props ); } } };
解析:
(1)先判断当前元素的 position 的值,没有设置 position 属性的话,默认为 relative,并获取元素的 top、left 属性的值
(2)返回一个对象 obj,obj 的 top 是参数的 top - 默认偏移(offset)的 top + position 设置的 top(没有设置,默认为0)
,obj 的 left 同理。
也就是说 offset({top:15,;eft:15}) 的本质为:
参数的属性减去对应的默认offset属性加上position上的对应属性。
//伪代码 offset().top = elem. getBoundingClientRect().top + document. pageYOffset top: offset({top:15}).top - offset().top + position.top 也就是: offset({top:15}).top - (elem. getBoundingClientRect().top + document. pageYOffset) + position.top
3、$()
.offset(function(){})
$("#pTwo").offset(function(index,currentoffset){ let newPos={}; newPos.left=currentoffset.left+15; newPos.top=currentoffset.top+15; return newPos; });
源码:
//如果传的参数是function{}的话 if ( isFunction( options ) ) { // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); }
解析:
让当前元素通过call 去调用参数中的 function(){} 方法,call 的参数必须一个一个放进去,上面源码中,call 参数有 i、jQuery.extend( {}, curOffset )
- jQuery.extend( {}, curOffset )
暂不解析jQuery.extend()
,但这里的作用 不用看源码,也知道是将 element.offset() 的属性赋值给新建的空对象 {} 。
所以 $().offset(function(){}) 的本质即:相对于 element.offset() ,对其 top,left进行操作,而不是像 offset({top:xxx,left:xxx}) 那样相对于左上角原点进行操作(这样就需要先减去offset()中的top、left的值了)。
本文结束,五一愉快~
(完)