这是因为 offset() 和 position() 完全不同,无论是在目的上还是在幕后。作者试图在the docs进行解释:
.position() 方法允许我们检索当前位置
一个元素(特别是它的边距框)相对于父偏移量
(特别是它的填充框,不包括边距和边框)。
将此与 .offset() 进行对比,后者检索当前位置
相对于文档。
但最好的解释是,像往常一样,在源代码中。 offset() getter 相当简单:检查该元素在活动 DOM 中的存在后,将其框与文档的框进行比较,通过滚动进行调整:
// If we don't have gBCR, just use 0,0 rather than error
// BlackBerry 5, iOS 3 (original iPhone)
if ( typeof elem.getBoundingClientRect !== "undefined" ) {
box = elem.getBoundingClientRect();
}
win = getWindow( doc );
return {
top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0),
left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
};
(顺便说一句,在 2.x 分支中更简单;不需要 gBCR 检查)
现在,position() getter相对很简单,仅当目标元素应用了position: fixed - 只需将 gBCR 结果作为偏移量。
否则,事情会变得一团糟。首先,算法应该定位“真正的”offsetParent - 位置不是静态的最近的前任。如果没有找到,则使用 documentElement 代替:
// key part of `offsetParent()` method
while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) &&
jQuery.css( offsetParent, "position" ) === "static" ) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || documentElement;
然后代码计算offsetParent 偏移量 - 如果它是documentElement,则使用其坐标,或者改为使用top: 0, left: 0。不要忘记边界!
var parentOffset = { top: 0, left: 0 },
// ... later on
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
parentOffset = offsetParent.offset();
}
// Add offsetParent borders
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
最后,比较偏移量 - 现在元素的边距也被考虑在内:
// Subtract parent offsets and element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
};
谜题的第三部分,offset() setter。以下是关键部分:
// set position first, in-case top/left are set even on static elem
if ( position === "static" ) {
elem.style.position = "relative";
}
curOffset = curElem.offset();
curCSSTop = jQuery.css( elem, "top" );
curCSSLeft = jQuery.css( elem, "left" );
calculatePosition = ( position === "absolute" || position === "fixed" ) &&
jQuery.inArray( "auto", [ curCSSTop, curCSSLeft ] ) > -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 {
curTop = parseFloat( curCSSTop ) || 0;
curLeft = parseFloat( curCSSLeft ) || 0;
}
// ...
if ( options.top != null ) {
props.top = ( options.top - curOffset.top ) + curTop;
}
if ( options.left != null ) {
props.left = ( options.left - curOffset.left ) + curLeft;
}
// ...
curElem.css( props );
本质上,它 - 再次 - 非常简单:计算元素的偏移量,并通过差异修改其 top 和 left。复杂的部分涵盖了position: fixed 与auto 值的组合top 或left 的情况。
在考虑position setter 的外观时,会出现两个问题。
首先,它应该如何处理静态元素?如上所示,offset setter 只是将他们的position 重写为relative;我们应该在这里走同样的路 - 并处理offsetParent的突然变化吗?
其次,每次调用setter时,它都应该重新计算offsetParent的填充框。当然,它在很大程度上取决于用例,但是——我们不应该只在元素挂载时才进行此计算,并在布局更改事件上重做吗?如果我们应该这样做,也许offset() setter 和现有的css({top, left}) 已经满足了我们的所有需求?
这些担忧可能解释了为什么这个选项还没有在 jQuery 中实现——尽管它已经存在于 jQuery UI 中。该插件不依赖于其他 jQuery UI 组件,但 its latest version 的长度约为 500 行。
如果您确实认为它也应该是 jQuery 的一部分,只需在 jQuery tracker 上提出相应的问题即可;至少我还没有找到anything related。