Safari Position:Sticky Not Working in an Overflow:Auto Element

Safari position:sticky not working in an overflow:auto element

I got this solution from someone else:

Basically, instead of position:sticky, use position:fixed for the right panel. The key is to also you will-change:transform in a parent div (in the above example, in .modal-content) so position:fixed becomes fixed relative to that parent, and not the viewport. It's a neat little trick

Position sticky not working in IE or Safari

Changing the position of #nb to relative fixes the issue.

#nb {
position: relative;
width: 280px;
height: 450px;
margin: 0 auto;
overflow: hidden;
text-align: center;

Why is my position:sticky not working on iOS?

I feel like an idiot for answering my own question, but I figured out what is causing the problem.
I hope this will help developers facing the same problem because I could not find anywhere defining this behavior.

As you can see, in my code, there is a wrapper (specifically a link) around the element, on which I use my position:sticky:

<a href="#" class="jobAlertToggle">
<div id="jobalarm_mobile">
<i class="fa fa-bell"></i>
<span>Jobalarm aktivieren</span>
<label class="switch">
<input type="checkbox">
<span class="slider round"></span>

For some reason, this is not a problem for Chrome or Firefox on Desktop as well as Android, as they seem to ignore this container, probably because it doesn't define any positioning behavior. This is why it works on other devices. However, iOS does not ignore the container and therefor positions the div relative to its parent container, which is the link. After removing the link for test purposes, it worked on all devices.

Workaround for a Safari position: sticky (-webkit-sticky) bug

I think I've figured it out. The trick is to put the entire children of the scrollable container (that is, the header and the content) into a wrapper div - then the bug isn't triggered.

How does the position: sticky; property work?

Sticky positioning is a hybrid of relative and fixed positioning. The element is treated as relative positioned until it crosses a specified threshold, at which point it is treated as fixed positioned.


You must specify a threshold with at least one of top, right, bottom, or left for sticky positioning to behave as expected. Otherwise, it will be indistinguishable from relative positioning.
[source: MDN]

So in your example, you have to define the position where it should stick in the end by using the top property.

html, body {
height: 200%;

nav {
position: sticky;
position: -webkit-sticky;
top: 0; /* required */

.nav-selections {
text-transform: uppercase;
letter-spacing: 5px;
font: 18px "lato", sans-serif;
display: inline-block;
text-decoration: none;
color: white;
padding: 18px;
float: right;
margin-left: 50px;
transition: 1.5s;

.nav-selections:hover {
transition: 1.5s;
color: black;

ul {
background-color: #B79b58;
overflow: auto;

li {
list-style-type: none;
<ul align="left">
<li><a href="#/contact" class="nav-selections" style="margin-right:35px;">Contact</a></li>
<li><a href="#/about" class="nav-selections">About</a></li>
<li><a href="#/products" class="nav-selections">Products</a></li>
<li><a href="#" class="nav-selections">Home</a></li>

Why does changing the height of a 'position:sticky' element alter the scroll position on Chrome but not Safari?

Why is this behaviour different on these two browsers?

To understand this, you first need to understand how a sticky element takes up space on a page. As you scroll past the sticky element, the rendered part of the sticky element follows the top of your viewport, but the element still physically takes up space where it was originally placed:

As the viewport scrolls down, it takes the rendered element with it, but the element still occupies space where it was originally placed.

This puts the browser in a bit of a weird situation when you resize the header, because it's actually an element that you have already scrolled past. There are two approaches to solving this. You can do like Safari and Firefox and always keep the same distance to the top of the document, or you can do like Chromium and move the viewport so that it follows the element you are currently looking at:

As the header increases in height, the two groups of browsers have different approaches to handling it.

I believe Safari and Firefox do it their way, because it's the easiest solution, and that's just how every browser has always done it.
I believe Chromium has changed it's approach recently because it has a distinct advantage when a user is reading articles with slow loading ads that change size after they've loaded:

As an ad loads and fills more, the content in the viewport shifts in Firefox and Safari, while it stays perfectly in place in Chromium.

As you can see above, when the ad loads in Safari and Firefox, it causes a major shift in the page content, which annoys and disorients the user. This can especially be bad if there are several ads that load shortly after one another.
With the Chromium approach, the viewport is adjusted so that the user doesn't even notice that anything has happened.

I was not able to find any spec or discussion to back up my claims, but i'm pretty sure that this is the reasoning behind. I am not sure if Firefox or Safari has had a reason to not implement Chromiums approach, or if they just didn't feel it was that important.

Which one, if any, is "correct"?

Which solution is best is probably a very complicated and somewhat subjective discussion that i won't go into, although the benefits of Chromiums approach is undeniable.

Is there any way to make both browsers behave identically (either way)?

When you press the Grow/Shrink button, you can always detect if the scroll height changes, and then set it back right after it was changed. You can also do the opposite and change the scroll height yourself so all browsers behave like Chromium. Here is an old article from someone who did exactly that:

Sticky HTML element gets hidden below the Mobile navigation bar in chrome / Firefox mobile browser. How to fix this? [ UPDATED ]

As a workaround, setting the top most layout component position to fixed solves the problem.

.layout {
display: block;
position: fixed;
height: 100vh;
width: 100vw;
max-height: 100%;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow-x: hidden;

working codepen ( open in mobile browser - Debug mode ):

Related Topics

Leave a reply