Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Position: sticky - scroll bouncing when combined with javascript height adjustment

After playing with position: sticky for a while, I started implementing it for sticky navigation and ran into this interesting, but frustrating scroll bouncing issue.

This is a common type of navigation behaviour seen on many sites, where you would traditionally use javascript to calculate offsets to a relative element in the page. When the element reaches the top of the window, a 'stuck' class would be added, taking the element out of the document flow with position: fixed, and a dummy element of the same height would be added in it's place to prevent the page from 'jumping'. Additionally, it's common to see javascript then shrink the height of that navigation to save space while scrolling.

CSS now seemingly takes care of all this with position: sticky, apart from (as far as I can tell), detecting when the element is 'stuck'. Instead I used some javascript to do the stuck detection, discovering that everything works great, right up until the height of the sticky element needs to change.

It's pretty hard to explain, but it wreaks havoc in production - so here's a stripped down example I've made to illustrate the issue as simply as possible.

CSS sticky position height adjustment bug

It's best illustrated when the height of the page is just the right length, so I've set a fixed height on the element to make sure everyone is able to see the same thing. You can add more content and it's still an issue when scrolling past.

The result is some really weird behaviour. When scrolling down, the navigation sticks, and as it shrinks the navbar, the 'dummy element' the browser is automatically creating courtesy of position: sticky seems to be kept in sync with it. That means, when the stuck class is added, the whole page gets smaller, and a fraction of a second later, the navigation is no longer stuck, thus resulting in a glitchy vibration loop.

The behaviour is also completely different across every browser I've tested. In chrome, this bouncing can never be resolved, it stays in the infinite loop constantly adding / removing the stuck class. More interestingly in Safari, the scroll position is 'pushed back' to a state where it wont bug out. Then in Firefox, it does both of these, glitching for a second or two, before forcing the scroll position back up again.

I'm wondering if anyone has experienced this, and come up with any solutions? Any js workaround I've come up with hasn't really worked or been very good! Surely as popularity grows, more people are going to hit this one...

Genius workarounds, hacks, insights, or perfect solutions all welcome!

like image 609
bluefantail Avatar asked Jun 15 '17 12:06

bluefantail


2 Answers

Try adding overflow-anchor: none; to the sticky element when applying changes that would alter its size (and potentially affect window size/element positioning).

Update: ultimately, the right solution I've hit on is: have an outer element that NEVER changes size (it's always the same full height at any given breakpoint). That one is made sticky. But it also should have no background/visual styles, and it's effective height should be defined by height + bottom margin (so that it takes up the right amount of initial space in the document, but doesn't actually block clicks once the visual nav shrinks and gives more space.

Then have an inner element that does change size, either in reality or just visually.

You can also use modern properties like contain: layout size; on the inner element like

like image 107
Dtipson Avatar answered Oct 06 '22 01:10

Dtipson


(Apparently you need more reputation to comment than answer ...)

This seems like a legitimate layout bug, so I'm curious what the opinion of browser contributors might be. Raised issues in the Chromium and Firefox bug trackers to see what'll happen:

https://bugs.chromium.org/p/chromium/issues/detail?id=734461 https://bugzilla.mozilla.org/show_bug.cgi?id=1374171

like image 31
Jono Mingard Avatar answered Oct 05 '22 23:10

Jono Mingard