Created by: cjweigle
Modals prevent shifting horizontally by compensating for the vertical scrollbar with padding. There are several issues in the detection logic for detecting if a vertical scrollbar is present.
Fixes #28101 (closed)
CHANGES
- When checking if the body is overflowing, use the scrollHeight of
<html>
, unless the body has overflow-y set to hidden. In that case, use the bottom of the boundary rect. - When showing the modal, call _adjustDialog after display has been set to 'block'
- In _adjustDialog, if compensation padding is no longer required due to window resize, remove previous compensation padding.
JUSTIFICATION
_checkScrollbar
1
Currently _checkScrollbar checks for horizontal overflow to detect if the vertical scrollbar is present. The behavior was changed in commit 32f62bc5 to work around an IE quirk. To check for the presence of a vertical scrollbar, the content height must be compared to the window height, not the widths.
2
Currently when setting _isBodyOverflowing, the width of the body including offset is calculated by adding the left position and the right position of the boundary box. This includes the left position twice as the right position includes the left position. The correct comparison checks only the right position. This comparison (modified to check vertical dimensions) is used in the patch when checking if the body is overflowing and the body has overflow-y set to hidden.
This behavior can be observed in the test: "should not add compensation padding when body boundary width (including negative margin-left) is greater than window width with no vertical scrollbar before showing and no vertical scrollbar after showing"
3
Currently when setting _isBodyOverflowing, the <body>
's boundary box is checked. The boundary box does not include overflowing descendant nodes. Checking <html>
's scrollHeight
includes the height of the content as well as margin on the <body>
.
This behavior can be observed in the test: "should add compensation padding when body boundary height is less than or equal to window height with vertical scrollbar before showing due to tall content and no vertical scrollbar after showing"
Initial _adjustDialog call
Currently _adjustDialog is called in the show() method before display is set to 'block'. Therefore the modals scrollHeight is always 0 when initially showing, which results in an incorrect value for isModalOverflowing in _adjustDialog. When _adjustDialog is called due to a resize event the scrollHeight is available as the modal is displayed. As such, _adjustDialog has different effects for similar conditions depending on whether it's the initial call or a resize call.
This behavior can be observed in the test: "should have consistent compensation padding when initially showing and after a resize event"
_adjustDialog
Currently _adjustDialog adds compensation padding as needed. The compensation padding is removed when the modal is hidden. _adjustDialog never removes the compensation padding, even when it is no longer required.
This behavior can be observed in the test: "should remove compensation padding no longer needed"
CHANGES TO TESTS
1
In the tests related to Modal compensation padding, the Bootstrap css distribution file is loaded. I have confirmed that different test outcomes occur when loading the css file and when not.
The synthetic toolbar used in the Modal tests does not accurately emulate the behavior of a browser scrollbar by showing and hiding as needed.
In _adjustDialog, the expression: !this._isBodyOverflowing && isModalOverflowing
can be thought of as "no vertical scrollbar before showing and vertical scrollbar after showing".
The expression: this._isBodyOverflowing && !isModalOverflowing
can be thought of as "vertical scrollbar before showing and no vertical scrollbar after showing".
The tests depend on the state transition of the scrollbar's visibility which is not emulated by the synthetic scrollbar.
2
The following tests required a 'tallContent' element to be added to correctly test their functionality now that compensation padding is correctly working: "should toggle a modal" "should adjust the inline padding of fixed elements when opening and restore when closing" "should adjust the inline margin of sticky elements when opening and restore when closing" "should properly restore non-pixel inline body padding after closing"
Demo
Current
Patched
Preview: https://deploy-preview-29320--twbs-bootstrap.netlify.app/docs/5.0/components/modal/