Popup position not correct when offsetWidth/offsetHeight == 0
See original GitHub issueCodeSandbox demo
https://codesandbox.io/s/gifted-sunset-ewyjj
Steps to reproduce the problem
- Using Chrome v92.0.4515.159
- Make sure that popperjs/core is in version 2.9.3
- Click on the red box, which is the trigger for displaying the pop-up
(The example is based on a largely more complex real use case, using Bootstrap, but I managed to recreate the behavior with that simple structure and pure Popper)
What is the expected behavior?
The popup should be displayed just below the red box, but is instead translated to the top left, partly outside of the container. The behaviour is correct in Firefox 91.0.2.
What went wrong?
The issue was introduced by fix #1247, which introduce additional checks on scaled elements.
There are 2 main characteristics in the constructed scenario that triggers the problem:
-
The parent container has non-integer margins (because of the use of fraction of rems:
margin: 1.1rem;
) that results in agetBoundingClientRect
rectangle with decimal width and/or height. On the contrary,offsetWidth
andoffsetHeight
return integers, so as the result the newisElementScaled
function returntrue
, as the numbers are not exactly the same. For instance, in my example,rect.width == 321.859375
, andelement.offsetWidth == 321
, hencerect.width / element.offsetWidth || 1 == 1.0026771806853583
, andscaleX !== 1
returntrue
-
Independently, it appears that Chrome does not compute any offsetWidth or offsetHeight for the
#popper-button
element in this scenario, and return 0 for both of them (this is the difference with Firefox, as the latter return values).
Putting those two characteristics together in
scaleX
and scaleY
are computed because includeScale == true
, and they are equal to Infinity
because element.offsetWidth == 0
and element.offsetWidth == 0
Then all values in the object returned by getBoundingClientRect
are equals to 0 because they are divided by Infinity
So, when used in getCompositeRect
function, with const rect = getBoundingClientRect(...)
The rect
components in the x
and y
output are 0, and - putting the scroll contribution aside - only the parent container offset negatively contributes, so the position is likely to be negative, therefore the translation to the top left corner.
Possible fixes
I am not familiar enough with the library to suggest the best course of action to prevent this bug, but I can see 2 directions:
isElementScaled
should maybe only compare integers, and round thegetBoundingClientRect
dimensions to integers. I guess that the initial fix was for significant scaling, not just when the bounding rectangle and the elements differs by a fraction of a pixel.- Ignore scaling when
element.offsetWidth
orelement.offsetHeight
return 0, and prevent theInfinity
scaling ratio.
Any other comments?
Sorry if the bug description lacks more understanding of the library, I am not familiar with it, as I use it only through Bootstrap. I just observed the reported behaviour and concluded this was down to Popper through Javascript troubleshooting in Chrome Dev Tools.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:5
- Comments:14 (8 by maintainers)
Top GitHub Comments
Can confirm same issue as @BrianHung (parent width resolving to .5 px), still present in latest version (2.10.1 at the time of this comment), fixed by reverting to 2.9.2.
May someone verify if this fixes your issue?
https://github.com/popperjs/popper-core/pull/1353