Skip to content

.position() and .offsetParent() return incorrect value #3107

@anseki

Description

@anseki

.position() method should return a position of element relative to a closest ancestor element that is positioned.
Therefore, for example, if CSS properties top/left of the element that has position:absolute are set to that returned position, the element should not move.
But when the ancestor element that is positioned does not exist (i.e. all ancestor elements have position:static, this is default), a position relative to <html> element incorrectly is returned.
For example:
https://jsfiddle.net/fqbgy5pq/

Result:

ClientRect   top: 63   left: 63
CSS props    top: auto left: auto
.position()  top: 59   left: 59
Set return value of `.position()` to `top` and `left`, then the element should not move.
ClientRect   top: 59   left: 59
CSS props    top: 59px left: 59px
.position()  top: 55   left: 55

Note: 2px was lost by this issue, and more 2px was lost by another issue of .offset().
When fixed .offset() is used:

Result:

ClientRect   top: 63   left: 63
CSS props    top: auto left: auto
.position()  top: 61   left: 61
Set return value of `.position()` to `top` and `left`, then the element should not move.
ClientRect   top: 61   left: 61
CSS props    top: 61px left: 61px
.position()  top: 59   left: 59

.offsetParent() method that is called by .position() method returns <html> when ancestor element that is positioned is not found. Then, .position() mistakes.

return offsetParent || documentElement;

I think that .offsetParent() should return document or null in this case.
document means "base of coordinates".
null means "ancestor element is not found".

Also, .position() method should return coordinates relative to the document in this case.

And also, .position() method returns incorrect coordinates when <html> has position:non-static (i.e. the ancestor element is really <html>).
For example:
https://jsfiddle.net/wczL6ae5/

Result:

ClientRect   top: 63   left: 63
CSS props    top: auto left: auto
.position()  top: 59   left: 59
Set return value of `.position()` to `top` and `left`, then the element should not move.
ClientRect   top: 62   left: 62
CSS props    top: 59px left: 59px
.position()  top: 58   left: 58

Note: 1px was added by this issue, and 2px was lost by another issue of .offset().
When fixed .offset() is used:

Result:

ClientRect   top: 63   left: 63
CSS props    top: auto left: auto
.position()  top: 61   left: 61
Set return value of `.position()` to `top` and `left`, then the element should not move.
ClientRect   top: 64   left: 64
CSS props    top: 61px left: 61px
.position()  top: 62   left: 62

When .offsetParent() method returned <html>, .position() method ignores its offset.

if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {

Therefore, if the <html> has offset (i.e. margin), returned value is incorrect.
Strange to say, only border-width is included in that calculation.
top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ),

I think that it might affect scripts that exist if the return value of .offsetParent() method is changed.
At least, .position() should be fixed even if .offsetParent() is not fixed. In this case, .position() should not use .offsetParent(), or it should check whether position is static when <html> was returned.
This issue affects .offset() method because setter of .offset() uses .position().

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions