different result of width() and height() since jQuery 3.0 #3193
Comments
|
Agreed! It's a good breaking change though, wouldn't you say? As far as documenting, perhaps we could add a sentence to the existing item here? Either that or create an entirely new "Breaking change" item for it. Rewording the heading will break existing links. |
HolgerJeromin
commented
Jun 22, 2016
|
.width() is now more correct, yes. |
HolgerJeromin
commented
Jun 22, 2016
|
The current implementation has one problem. Setting and Getting via css() is not symmetric anymore:
|
|
Good point, and that doesn't seem good. Not sure how to deal with it though. It would help if |
HolgerJeromin
commented
Jun 22, 2016
•
|
Applications like mine have the defect right now. |
|
jQuery's |
dmethvin
added the
Needs review
label
Jun 27, 2016
HolgerJeromin
commented
Jun 29, 2016
|
The change is still not in the upgrade guide. IMO this should be done as fast as possible to prevent upgraders to have the same problems as we had. |
|
The reason no change has been made is because we haven't yet decided whether we should change code or docs. Once we do this ticket will be closed. |
HolgerJeromin
commented
Jun 30, 2016
|
Thanks for the explanation. |
timmywil
added the
Dimensions
label
Jun 30, 2016
Getfree
commented
Jul 8, 2016
|
If I may add my opinion.... What you are doing with this change is mixing this two concepts, so now there is no way of consistently getting the dimensions of an element. Rather, we get the dimensions or the bounding box depending on whether there are CSS transformations applied or not. It's an acceptable breaking change if .width() and .height() now return the bounding box rather than the dimensions as long as we have a way of getting the true dimensions. Do we have such a way? Also, .css() is supposed to give the computed css properties (hence its name), but if now .css('width') and .css('height') give the bounding box instead, that's not just a breaking change, that's messing with the user base. It's just a huge gotcha. |
HolgerJeromin
commented
Jul 8, 2016
•
|
Just to add another point regarding relationship of
This is documented behavior, have not checked if this is still valid for jQuery 3.0. |
|
I think those are all valid points. As far as resolving the problem, there are conflicting concerns here, I'll just mention width but it applies to height as well.
@HolgerJeromin @Getfree What would you like these APIs to return? Let's start with that and think about how existing code might break. |
Getfree
commented
Jul 8, 2016
|
The way I see it, it's essential that we have a reliable way of getting the dimensions and position of an element no matter if the element itself or an ancestor is css-transformed. Consider this example: The blue box wants to be exactly under the red box. But since the BODY element is transformed, bounding-box coordinates are no good. In general, from the moment you apply css-transformations, any calculation based on elements dimensions is going to give the wrong result. I'm Ok with jQuery 3 introducing breaking changes as long as there is a way of getting true element's dimensions (and not bounding-box dimensions) when they are needed. |
HolgerJeromin
commented
Jul 11, 2016
•
Exactly. My application needs a way to get dimension based information for correct positioning of complex transformed elements. Non fractional value is no disadvantage if result is not complete bogus after transforming. |
|
Hmm, maybe we need to use Are there any cases where you'd want the dimensions with transforms applied? |
|
Let's start with a summary of documented surface area (using the horizontal dimension without loss of generality):
All of these should be capable of returning fractional values, but—since they are so closely tied to the CSS box model and especially since they're all also setters—ignore transforms. In fact, the non- For this ticket, though, I have to agree with @HolgerJeromin. We should not use |
gibson042
added this to the
3.1.1
milestone
Jul 11, 2016
gibson042
removed the
Needs review
label
Jul 11, 2016
Getfree
commented
Jul 13, 2016
|
Also remember that Example: https://jsfiddle.net/au6uem3p/ |
|
@Getfree I wouldn't say they give "wrong" results as they do return the element displayed position. Going back to basics, I was wondering what are the main questions being asked that jQuery (or a browser API for that matter) should answer to. I see 3 of them related to
The browser APIs have been evolving in a way that should satisfy the above conditions. There is no API to get the displayed size of the element but without taking transitions into account; asking for something like that is kind of weird, most of the time people asking this question are really asking for the value of the computed width, not the displayed width minus transforms. AFAIK there is no browser API that would answer this question as well. There is Now, as for the last point - our problem is that the If we want to leave |
|
I've tried removing the I'm not sure if there's anything we can do before 4.0. |
|
Going back to |
|
Seems like @mgol has explained the challenges here pretty well. Any API that retrieves the actual transformed dimension or position as a single number is taking several CSS properties into account and can't be used as both a getter and setter to round-trip that single number. What can we do before 4.0? I'd consider some of this to be regressions so even if it changes existing behavior for better compatibility with 2.x it still may be in play for a 3.x.0 release. |
bzbarsky
commented
Jul 13, 2016
Browsers have There is no browser API for returning the "computed width" in CSS terms. Put another way, if you have:
|
I actually meant the bounding box of the element i.e. "what's the size of the box that appears on the screen", so including transforms.
I meant "resolved width"; I keep using the wrong name because of how
I meant "offsetWidth" here, I keep mixing stuff, d'oh. Post corrected. Basically, my point was that you may either ask for a resolved value for a specific CSS property, here: |
bzbarsky
commented
Jul 13, 2016
|
Yes, that's a correct summary of the state of browser API. |
|
|
Considering the impact of some of these changes, moving to 3.2.0. We'll get smaller issues out in a 3.1.1 first. |
timmywil
modified the milestone: 3.2.0, 3.1.1
Jul 13, 2016
By reverting them to
I'll reiterate that I consider those setters to be bad APIs due to having layout thrashing built in. We've tried to drastically simplify
This sounds good to me. |
No,
It's possible, but as I mentioned above, I believe these methods to be the most convenient means of interacting with the CSS box model—getting rid of the setter logic would leave a vacuum. And note that the layout thrashing only comes into play with property/box-sizing mismatches (e.g., |
|
I mentioned in one of my comments why I think switching to gCS would be a breaking change; we currently guarantee css('width') works even on detached elements. |
|
a) we should probably deprecate that |
|
Our heroic efforts to get the dimensions of detached or hidden elements were (in retrospect) a mistake IMO. Deprecating them ASAP seems like a good idea and warning in Migrate if we can, but I suspect it will be a while before we could remove it. |
JoolsCaesar
commented
Jul 29, 2016
•
|
Are you aware that this breaks draggables in jquery UI 1.11.4?
So draggables grow/shrink whenever you move them. I have yet to test 1.12. |
Wykks
commented
Aug 9, 2016
|
It's breaking jquery-ui 1.12 draggable / resizable too |
JoolsCaesar
commented
Aug 10, 2016
|
In what way? Upgrading to 1.12 fixed the problem I was facing, as the weird width=width line has been removed. |
Wykks
commented
Aug 10, 2016
•
|
Sorry only resizable, not draggable. |
jQuery UI has never supported transforms on interactions. |
craigkovatch
commented
Aug 27, 2016
•
|
We just upgraded to jQuery 3 and have some hidden bugs because getting/setting with .height() is now asymmetric due to a scaling transform on a root element. Chiming in to say this is not a "positive breaking change" and agree a different method (or flag) should be added so I can opt-in to getting "bare" or "transformed" dimensions. But whichever route is taken, please make sure .height() and .height(x) work symmetrically. |
HolgerJeromin
commented
Aug 30, 2016
|
The upgrade guide still does not mention this issue as a breaking change! 8-/ |
|
@HolgerJeromin It doesn't mention it because we consider it a bug that we'd like to fix in jQuery 3.2.0. |
craigkovatch
commented
Aug 30, 2016
|
@mgol shouldn't it still be documented? Otherwise people have to discover it for themselves via breaking bugs. |
|
This ticket is the documentation that it's a bug and that we plan on fixing it. |
timmywil
added the
Blocker
label
Sep 26, 2016
timmywil
self-assigned this
Sep 26, 2016
This was referenced Oct 17, 2016
HolgerJeromin
commented
Nov 8, 2016
|
BTW: Some Release Notes have a section known issues for such things. Just wanted to add that the Syntax |
sabaca
commented
Nov 22, 2016
|
you can use something like that (for jquery-3.1.1-min.js): |
craigkovatch
commented
Nov 29, 2016
|
@dmethvin that's a disappointing response. It places the onus on every developer to search through all open issues in jQuery rather than including it in e.g. a 'breaking changes/known issues' section of release notes. |
|
@craigkovatch I think we all had hoped it could be fixed more quickly. Perhaps we need to fall back to assuming this won't be fixed in 3.x and will have to wait for 4.x since it's a breaking change. Would you like to submit an addition to https://github.com/jquery/jquery.com/blob/master/pages/upgrade-guide/3.0.md to document this? |
craigkovatch
commented
Nov 29, 2016
|
@dmethvin I'm concerned that breaking in 3.x and restoring in 4.x is going to make the situation even worse. Really hoping this can still end up in a 3.x release. I'm happy to document in the upgrade-guide. Would this be a candidate for inclusion in the migration plugin? |
workmanw
commented
Dec 2, 2016
|
We were burned by this :(. Our app offers a whiteboarding-like tool that has drag/drop, element resize, and scaling. This change definitely caught us off guard. There is a lot of great discussion here. The only thing I'll add is that it's very unexpected that I definitely see how this is kind of quagmire because you don't want to make a breaking change for 3.x. Would you guys consider adding an argument to |
|
I think we know what the 3.x fix looks like, it's just a matter of someone volunteering the time to implement it (most of the core contributors, myself included, have been pulled in other directions in the latter half of this year). |
|
I too have code breaking due to this undocumented backwards incompatibility. |
vanderlee
referenced
this issue
in vanderlee/coverflow
Dec 5, 2016
Closed
doesn't work with jQuery 3.1.1? #36
workmanw
commented
Dec 5, 2016
|
@vanderlee As a work around, I added the following utility functions to our application. function _jQuerySize(elem, name) {
if (typeof elem === 'string') {
elem = jQuery(elem);
}
if (elem instanceof jQuery) {
elem = elem[0];
}
let val = jQuery.style(elem, name);
return parseFloat(val);
}
export function outerWidth(elem) {
return _jQuerySize(elem, 'width');
}
export function outerHeight(elem) {
return _jQuerySize(elem, 'height');
}Then to use it: import { outerWidth } from 'utils/jquery.js';
outerWidth('.user-item');
// or
let $userItem = $('.user-item');
outerWidth($userItem);You could probably even monkey patch jQuery if you have 3rd party libraries that depend on this behavior ... but that felt a bit dangerous. |
This was referenced Jan 5, 2017
roeycohen
commented
Feb 15, 2017
|
you may find alternative implementations of width of height functions in this site: |
This was referenced Mar 2, 2017
timmywil
added the
Has Pull Request
label
Mar 13, 2017
timmywil
closed this
in c920ff6
Mar 13, 2017
This was referenced Mar 16, 2017
|
The fix for this issue brought a regression: #3571. It seems we don't have a good Web API to satisfy our needs:
Is there any way to retrieve a fractional "real" value for width on inline elements that doesn't take transforms into account? @bzbarsky? |
Updated example to show that transforms can be just as well expected as unexpected. Then positioning something near another element, for example (using jQuery 3.1.1): Here, the behaviour toward So in the end, there is no way to make it work for everything because crucial knowledge about the context is not given to the method. This is a lot like the difference between |
|
Another idea for a partial fix for 3.2.1 - keep the current logic but
fallback to offsetWidth if gCS returns auto (unless offsetWidth is
undefined, then fallback back to auto). The negative effect would be that
we'd lose subpixel values for width on inline elements. Also, inline SVG
elements would keep the broken .width() === 0 but we've never supported
that use case.
Timo: the problem with including transforms is that the same code handles
.css('width') where taking transforms into account is definitely unexpected.
--
Michał Gołębiowski
|
bzbarsky
commented
Mar 17, 2017
Not that's shipping in browsers. In fact maybe even that's not shipping; I thought that that https://drafts.csswg.org/cssom-view/#dom-geometryutils-getboxquads using the node itself as the relativeTo value might do the trick, but you'd probably still get post-transform sizes. (I should note that asking for the "width" of a non-replaced inline is a fairly odd question, especially as soon as there's a linebreak in the middle of it.) |

HolgerJeromin commentedJun 22, 2016
Just to provide feedback:
#2439 has not only the impact returning non-integer values.
I have a CSS transform:scale(2) in a root element with a div which has css width 200px.
jQuery 2.x returns 200 for .width() as it uses offsetWidth
jQuery 3.0 returns 400 for .width() as it uses getBoundingClientRect()
This is a breaking change which should be at least mentioned in the upgrade guide.