Google Chrome viewport-anchored expand direction with flexbox
There is an issue in Google Chrome where elements expand/collapse in different directions relative to the viewport when placed inside a flexbox container with an adjacent flex item having space-between
or center
justified content.
This is not a problem in Firefox, IE11, Edge, or Safari as the elements always expand downward.
I'm curious:
- Does Chrome's behavior here follow some spec? If so, which one?
- If not, then why was this done in Chrome? (IMHO, it is horrible UX for the click trigger to disappear offscreen randomly.)
- Can Chrome's behavior be modified or disabled in someway? Eg. through CSS or meta-tag?
Update 1: I've filed issue #739860 on chromium bug tracker seeking insight/explanation from Chromium devs, if possible.
Here are two examples inserted below. The full test suite describing the problem can be found in this pen: https://codepen.io/jameswilson/full/xrpRPg/ I've chosen to use slideToggle in this example so that the expand/collapse motion is animated and visible to the eye. The same behavior happens with the details tag, but cross-browser support is not there yet, and the expand/collapse is too janky.
Ex 1: Chrome's expand/collapse behavior for space-between justified flexbox
$('button').click(function() {
$(this).next().slideToggle();
})
body {
margin: 30px;
font-family: sans-serif;
}
aside,
aside div,
summary,
main,
button,
details p,
button + p {
background: rgba(0,0,0,.09);
border: none;
padding: 20px;
margin: 0;
}
.flexcontainer {
display: flex;
}
aside {
flex: 1;
position: relative;
max-width: 25%;
background: mintcream;
display: flex;
flex-direction: column;
position: relative;
}
aside.space-between {
justify-content: space-between;
}
aside.center {
justify-content: center;
}
main {
flex: 3;
position: relative;
max-width: 75%;
background: aliceblue;
vertical-align: top;
height: 100%;
}
main > * + * {
margin-top: 20px;
}
button + p {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="flexcontainer">
<aside class="space-between">
<div>Top Sidebar</div>
<div>Bottom Sidebar</div>
</aside>
<main>
<div>
<button>slideToggle</button>
<p>Other browsers: always expands downward.<br>
Chrome: Should always expand downward because Top Sidebar is always visible.</p>
</div>
<div>
<button>slideToggle (usually expands down)</button>
<p>Other browsers: always expands downward.<br>
Chrome: Should expand downward while Bottom Sidebar and Top Sidebar are both visible. But will expand upward if you scroll down far enough so that Top Sidebar is off-screen.</p>
</div>
<div>
<button>slideToggle (usually expands down)</button>
<p>Other browsers: always expands downward.<br>
Chrome: Should expand downward while Bottom Sidebar and Top Sidebar are both visible. But will expand upward if you scroll down far enough so that Top Sidebar is off-screen.</p>
</div>
</main>
</section>
Ex 2: Chrome's expand/collapse behavior for center justified flexbox
$('button').click(function() {
$(this).next().slideToggle();
})
body {
margin: 30px;
font-family: sans-serif;
}
aside,
aside div,
summary,
main,
button,
details p,
button + p {
background: rgba(0,0,0,.09);
border: none;
padding: 20px;
margin: 0;
}
.flexcontainer {
display: flex;
}
aside {
flex: 1;
position: relative;
max-width: 25%;
background: mintcream;
display: flex;
flex-direction: column;
position: relative;
}
aside.space-between {
justify-content: space-between;
}
aside.center {
justify-content: center;
}
main {
flex: 3;
position: relative;
max-width: 75%;
background: aliceblue;
vertical-align: top;
height: 100%;
}
main > * + * {
margin-top: 20px;
}
button + p {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="flexcontainer">
<aside class="center">
<div>Center Sidebar</div>
</aside>
<main>
<div>
<button>slideToggle (usually expands downwards)</button>
<p>Other browsers: always expands downward.<br>
Chrome: Usually expands downwards. Expands in both directions when the top-edge of the container scrolls out of the viewport.</p>
</div>
<div>
<button>slideToggle</button>
<p>Other browsers: always expands downward.<br>
Chrome: Usually expands downwards. Expands in both directions when the top-edge of the container scrolls out of the viewport.</p>
</div>
<div>
<button>slideToggle (usually expands downwards)</button>
<p>Other browsers: always expands downward.<br>
Chrome: Usually expands downwards. Expands in both directions when the top-edge of the container scrolls out of the viewport, but then resumes expanding downwards again when Center Sidebar scrolls out of view.</p>
</div>
</main>
</section>
Solution 1:
Add this code to your flex container:
overflow-anchor: none
This will disable a feature in Chrome known as "scroll anchoring", which is causing the upward expansion of boxes (revised codepen).
In Chrome, the upward / downward direction of expanding boxes is governed by the scroll position in the viewport, not the layout itself.
Chrome takes a unique approach to this behavior for the purpose of improving the user experience.
Basically, they bind a DOM element to the current scroll position. The movement of this particular ("anchor") element on the screen will determine an adjustment, if any, to the scroll position.
They call this approach "Scroll Anchoring". The algorithm is explained on this page: https://github.com/WICG/ScrollAnchoring/blob/master/explainer.md
This behavior is currently unique to Chrome, but Google is pushing for standardization.
In accordance with the Scroll Anchoring documentation, applying overflow-anchor: none
to the appropriate element(s) will disable scroll anchoring adjustments.
More information:
- https://github.com/WICG/ScrollAnchoring/blob/master/explainer.md
- https://bugs.chromium.org/p/chromium/issues/detail?id=739860
- https://hacks.mozilla.org/2019/03/scroll-anchoring-in-firefox-66/
- Changing CSS flex order causes scrolling