CSS "hook" for left and top styles makes no sense
See original GitHub issueDescription
Nothing more pointless in browser scripting than trying to compute styles. The document.defaultView.getComputedStyle
(incorrectly referenced as window.getComputedStyle
in jQuery) method is a hideously complex black box that virtually never needs to be opened. In twenty years of browser scripting, I’ve managed to avoid it almost entirely (exception that comes to mind was an HTML editor).
Regardless, jQuery aspires to fix the whole thing and frequently gets into trouble. Consider this “hook” for the top
and left
styles:
// Support: Safari <=7 - 9.1, Chrome <=37 - 49
// Add the top/left cssHooks using jQuery.fn.position
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
// getComputedStyle returns percent when specified for top/left/bottom/right;
// rather than make the css module depend on the offset module, just check for it here
jQuery.each( [ "top", "left" ], function( i, prop ) {
jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
function( elem, computed ) {
if ( computed ) {
computed = curCSS( elem, prop );
// If curCSS returns percentage, fallback to offset
return rnumnonpx.test( computed ) ?
jQuery( elem ).position()[ prop ] + "px" :
computed;
}
}
);
} );
The cryptically named support.pixelPosition
flag refers to a test result where a percentage-based top
style fails to compute to pixel units. This is an issue that dates back to the bad old days of IE 8-, where currentStyle
was frequently used by libraries as a slightly-off substitute for getComputedStyle
; it didn’t convert any units to pixels. jQuery certainly does this in older versions, so the problem should be familiar.
This is the part that attempts to fix the issue:
// If curCSS returns percentage, fallback to offset
return rnumnonpx.test( computed ) ?
jQuery( elem ).position()[ prop ] + "px" :
computed;
It calls the position
method instead, which attempts to figure the offset of the element from the document origin, which would only be equivalent to the computed left
and top
styles in only a small subset of cases.
For example, an element with a position style of absolute
with all parent elements having position: static
would work. On the other hand, an element with fixed
position would fail unless the document’s scroll position is its origin. Furthermore, an element with relative
position would fail in virtually every case. And so on, this “hook” will simply cause browsers demonstrating this quirk to return nonsensical position values in numerous cases (not even considering problems related RTL documents).
For those who never bothered to read my “P is for Position” primer on the old cinsoft.net site (now retired) or the numerous Usenet articles I authored on this subject (dating back to at least 2008), I’ll go over the general solution again.
In short:
- Save the
left
ortop
style (e.g.elem.style.left
). - Get the offset.
- Set the
left
ortop
style to'0'
. - Get the offset again.
- Find the difference between the two offsets.
- Put the old
left
ortop
style back.
And as the offset
method is just a pointless wrapper for getBoundingClientRect
(seems to exist only to handle calls passing disembodied elements or elements with a display style of none
), would advise simply calling getBoundingClientRect
and documenting that trying to compute position styles (or offsets) for such elements will result in undefined behavior (a recurring theme here). In addition, to let callers know they made a mistake at the earliest possible time, throw an exception in such cases (as opposed to returning zeros).
HTH
PS. What about right
and bottom
positions? The solution as it sits is only half-finished. Luckily I archived the related primer years ago:
https://github.com/david-mark/My-Library/blob/master/position.html
PPS. As this is an old issue that surely affects previous jQuery versions, am curious as to how the old versions are kept up to date when bugs like this are fixed.
Link to test case
No test case needed. Logic is clearly incorrect.
Issue Analytics
- State:
- Created 7 years ago
- Comments:19 (8 by maintainers)
A “bug report” that finishes with “No test case needed. Logic is clearly incorrect” is already starting off on the wrong foot. Start with a test case that clearly fails, then we can talk about whether/why the code is wrong and how to fix it. The test case will end up in unit tests and we will all be happy.
@ctomczyk: If @david-mark (or anyone else) provides a test case demonstrating the bug, we will reopen this issue.