一、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 的值了)。
本文结束,五一愉快~
(完)