【问题标题】:why jquery position() method does not have a setter version为什么 jquery position() 方法没有 setter 版本
【发布时间】:2018-04-11 04:02:03
【问题描述】:

我目前正在研究 jquery,并注意到 jquery 中的大多数方法都有设置和 getter 版本。即该方法用作 setter 或 getter,具体取决于传递给该方法的参数的类型/数量。

jquery offset() method 就是一个例子。

但我也注意到jquery position() method 只有一个 getter 版本,并且缺少它的 setter 版本。

有人知道为什么position() 方法没有setter 版本吗?我不太确定,但我认为 setter 版本在某些情况下会很有用。

【问题讨论】:

  • 可能是因为offsetTopoffsetLeft 是只读属性
  • 那么如果我们不能设置offsetTopoffsetLeft属性,会有什么影响呢?例如,在任何情况下设置 offsetTopoffsetLeft 可能有用吗?
  • 因为元素的位置根据 css 位置属性的不同而表现不同,而且 jquery 有 css 位置设置器,所以无论如何它都是多余的,并且无缘无故地增加了库的大小。

标签: javascript jquery


【解决方案1】:

这是因为 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 );

本质上,它 - 再次 - 非常简单:计算元素的偏移量,并通过差异修改其 topleft。复杂的部分涵盖了position: fixedauto 值的组合topleft 的情况。


在考虑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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    • 1970-01-01
    • 2012-06-19
    相关资源
    最近更新 更多