Google Chrome viewport-anchored expand direction with flexbox

JamesWilson

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

chrome's expand/collapse behavior for space-between justified

$('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

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>

Michael Benjamin

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:

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

TOP Ranking

  1. 1

    Failed to listen on localhost:8000 (reason: Cannot assign requested address)

  2. 2

    Loopback Error: connect ECONNREFUSED 127.0.0.1:3306 (MAMP)

  3. 3

    How to import an asset in swift using Bundle.main.path() in a react-native native module

  4. 4

    pump.io port in URL

  5. 5

    Compiler error CS0246 (type or namespace not found) on using Ninject in ASP.NET vNext

  6. 6

    BigQuery - concatenate ignoring NULL

  7. 7

    ngClass error (Can't bind ngClass since it isn't a known property of div) in Angular 11.0.3

  8. 8

    ggplotly no applicable method for 'plotly_build' applied to an object of class "NULL" if statements

  9. 9

    Spring Boot JPA PostgreSQL Web App - Internal Authentication Error

  10. 10

    How to remove the extra space from right in a webview?

  11. 11

    java.lang.NullPointerException: Cannot read the array length because "<local3>" is null

  12. 12

    Jquery different data trapped from direct mousedown event and simulation via $(this).trigger('mousedown');

  13. 13

    flutter: dropdown item programmatically unselect problem

  14. 14

    How to use merge windows unallocated space into Ubuntu using GParted?

  15. 15

    Change dd-mm-yyyy date format of dataframe date column to yyyy-mm-dd

  16. 16

    Nuget add packages gives access denied errors

  17. 17

    Svchost high CPU from Microsoft.BingWeather app errors

  18. 18

    Can't pre-populate phone number and message body in SMS link on iPhones when SMS app is not running in the background

  19. 19

    12.04.3--- Dconf Editor won't show com>canonical>unity option

  20. 20

    Any way to remove trailing whitespace *FOR EDITED* lines in Eclipse [for Java]?

  21. 21

    maven-jaxb2-plugin cannot generate classes due to two declarations cause a collision in ObjectFactory class

HotTag

Archive