一、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 );
} );
}
//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()
,但这里的作用 不用看源码,也知道是将 element.offset() 的属性赋值给新建的空对象 {} 。所以 $().offset(function(){}) 的本质即:相对于 element.offset() ,对其 top,left进行操作,而不是像 offset({top:xxx,left:xxx}) 那样相对于左上角原点进行操作(这样就需要先减去offset()中的top、left的值了)。