CSS “hook” for left and top styles makes no sense

Description

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:

  1. Save the left or top style (e.g. elem.style.left).
  2. Get the offset.
  3. Set the left or top style to '0'.
  4. Get the offset again.
  5. Find the difference between the two offsets.
  6. Put the old left or top 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.

Author: Fantashit

4 thoughts on “CSS “hook” for left and top styles makes no sense

  1. Has a user reported a bug about this?

    Why fixing bug is determined by the moment until the user report the bug? If somebody find the bug and it is not reported by any user does it means that the problem never occurred for any user? I am just trying to understand the process of workflow.

    From my experience I see that the most of the users (including developers) will never report any bugs instead they will fix it “locally” as they need it quite fast and don’t want to wait until specific owners/group of people will fix it.

  2. Has a user reported a bug about this?

    Why fixing bug is determined by the moment until the user report the bug?

    The biggest problem with this issue (& many others) is that it conflates many issues into one and is not clear about effects requested changes. It’s really hard to follow such issues, as they promote digressions in the discussion. If someone reports a separate issue which clearly requests one API change with clear and succint description, preferably with short code snippets showing the change then we’d consider.

    From my experience I see that the most of the users (including developers) will never report any bugs instead they will fix it “locally” as they need it quite fast and don’t want to wait until specific owners/group of people will fix it.

    That’s true but if an issue affects many people, someone will usually report it in the end. We can’t be second-guessing the users of the library.

  3. 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.

Comments are closed.