How does the "position: sticky;" property work?
Solution 1
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 oftop
,right
,bottom
, orleft
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;
}
<nav>
<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>
</ul>
</nav>
Solution 2
Check if an ancestor element has overflow set (e.g. overflow:hidden
); try toggling it. You may have to inspect the DOM tree higher than you expect =).
This may affect your position:sticky
on a descendant element.
Solution 3
I have same problem, and i found the answer here.
If your element isn't sticking as expected the first thing to check are the rules applied to the container.
Specifically, look for any overflow property set on any parents of the element. You can't use: overflow: hidden
, overflow: scroll
or overflow: auto
on the parent of a position: sticky
element.
Solution 4
Incase you came across this and your sticky is not working - try setting the parent to:
display: unset
Worked for me
Solution 5
Few more things I've come across:
When your sticky element is a component (angular etc)
-
If the 'sticky' element itself is a component with a custom element-selector, such as an angular component named
<app-menu-bar>
you will need to add the following to the component's css::host { display: block; } // or use flexbox
or
app-menu-bar { display: block; } // (in the containing component's css)
Safari on iOS in particular seems to require `display:block` even on the root element `app-root` of an angular application or it won't stick.
-
If you are creating a component and defining the css inside the component (shadow DOM / encapsulated styles), make sure the
position: sticky
is being applied to the 'outer' selector (eg.app-menu-bar
in devtools should show the sticky position) and not a top leveldiv
within the component. With Angular, this can be achieved with the:host
selector in the css for your component.:host { position: sticky; display: block; // this is the same as shown above top: 0; background: red; }
Other
-
If the element following your sticky element has a solid background, you must add the following to stop it from sliding underneath:
.sticky-element { z-index: 100; } .parent-of-sticky-element { position: relative; }
-
Your sticky element must be before your content if using
top
and after it if usingbottom
. -
There are complications when using
overflow: hidden
on your wrapper element – in general it will kill the sticky element inside. Better explained in this question -
Mobile browsers may disable sticky/fixed positioned items when the onscreen keyboard is visible. I'm not sure of the exact rules (does anybody ever know) but when the keyboard is visible you're looking at a sort of 'window' into the window and you won't easily be able to get things to stick to the actual visible top of the screen.
-
Make sure you have:
position: sticky;
and not
display: sticky;
Misc usability concerns
- Be cautious if your design calls for for sticking things to the bottom of the screen on mobile devices. On iPhone X for instance they display a narrow line to indicate the swipe region (to get back to the homepage) - and elements inside this region aren't clickable. So if you stick something there be sure to test on iPhone X that users can activate it. A big 'Buy Now' button is no good if people can't click it!
- If you're advertising on Facebook the webpage is displayed in a 'webview' control within Facebook's mobile apps. Especially when displaying video (where your content begins in the bottom half of the screen only) - they often completely mess up sticky elements by putting your page within a scrollable viewport that actually allows your sticky elements to disappear off the top of the page. Be sure to test in the context of an actual ad and not just in the phone's browser or even Facebook's browser which can all behave differently.
Harleyoc1
Updated on February 03, 2022Comments
-
Harleyoc1 over 2 years
I want to make the navigation bar stick to the top of the viewport once a user scrolls the page, but it's not working and I have no clue why. If you can please help, here is my HTML and CSS code:
.container { min-height: 300vh; } .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; }
<main class="container"> <nav style="position: sticky; position: -webkit-sticky;"> <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> </ul> </nav> </main>
-
ViliusL about 6 yearsIn general, this answer is not enough to sticky to work, parent also should not have overflow property.
-
Marvin about 6 yearsThank you for your comment. I agree with you that for other examples than the OP's my answer might not be sufficient. The other answers point this out :)
-
aruno almost 6 years@Sergey I'm not absolutely clear which of
display: block
andposition: relative
triggers the correct behavior in Safari - did it seem that onlydisplay: block
was needed? Of course it probably doesn't hurt to have both specified -
Sergey almost 6 yearsJust
display: block
was enough -
danday74 almost 6 yearsThis is the right answer. However, it can be difficult to find which parent element is causing the problem. I wrote a jquery script to help identify the overflow property on parents which identified the problem (just run it in your console). Because the script is a few lines I've added an answer containing the script below.
-
danday74 almost 6 yearsThis is also the right answer but also see my answer which helps to easily identify the parent causing the problem.
-
Cedric Ipkiss almost 6 yearsI missed the "s" in your "parent elements"
-
Mariusz Pawelski almost 6 yearsI also missed the "s":( and that was the issue. In many places people write that you should check only parent not all parents. I found my issue by checking if calling
$(stickyElement).parents().css("overflow", "visible")
in web console would help and it did. Then I located the distant parent withoverflow:hidden
style that caused the problem. I wish I read this answer more carefully. -
Mariusz Pawelski almost 6 yearsYou should check not only closest parent container but all parent elements.
-
danday74 almost 6 yearsyes, see my answer to do exactly that and check ALL parents very quickly
-
David Brower over 5 yearsThat was very helpful. I was able to quickly identify the parent element that had a value of
overflow: invisible
. -
lmiller1990 over 5 yearsThe link helped me, too. My problem was solved by "...If you're not using overflow and still having problems it's worth checking if a height is set on the parent..."
-
magikMaker over 5 yearsIf you don't have jQuery on your page, you can run this small snippet in the console to add it:
(async function() { let response = await fetch('https://code.jquery.com/jquery-3.3.1.min.js'); let script = await response.text(); eval(script); })();
-
AJMansfield over 5 yearsDo you have a source for this, or any other information about how to work around this?
-
Mariusz Pawelski over 5 yearsYou can also run this snippet that doesn't require JQuery to find non "visible" overflow parent:
var p = $0.parentElement; while(p != null) { var ov = getComputedStyle(p).overflow; if(ov !== 'visible') console.warn(ov, p); else console.log(ov, p); p = p.parentElement; }
Where$0
is the last element selected in developer tools. -
saglamcem over 5 yearsThank you, this was what I was looking for! For me there were no parents with overflow, nor were there any height issues. It'd be nice to see this info in the accepted answer as well.
-
code4j about 5 yearsI think the trick is that position sticky can only be applied to the children which belongs to a scrollable parent with a known height, (a direct children in a flexbox has a know height which is computed from other flex-item)
-
J.McLaren almost 5 yearsVery important that you may have to go higher up the DOM tree than expected. I usually set my <html> tag to overflow-x: hidden and this can prevent vertical sticky behavior way down the DOM tree.
-
brandonjp almost 5 yearsIf you need check for
overflow:hidden
, you can run this script in your browser console to check all parents/ancestors of a given element: gist.github.com/brandonjp/478cf6e32d90ab9cb2cd8cbb0799c7a7 -
Rahul Singh over 4 yearsLife saver, really simple description to the problem , but is there a work around to this just curious
-
Benjamin Intal over 4 yearsEven
overflow-x: hidden
will stop the element from sticking. However,overflow-x: hidden
in the body tag will work. -
Ángel Jiménez over 4 years@BenjaminIntal not in my case, I had this overflow-x: hidden in the body tag and it was the reason it was blocking it to work properly.
-
MaciejLisCK over 4 yearsdisplay: flex makes the thing
-
Piotr Szlagura over 4 years@ViliusL thank you for your comment! That was the problem I had with position: sticky, I wouldn't have known that if not for you as I didn't find this information anywhere.
-
Rohan Shenoy over 4 yearssticky position should not have a parent with overflow property, also sticky is not supported in some web browsers
-
Rohan Shenoy over 4 yearsIf you're implementing this solution on a table where you need to freeze the columns and you have overflow property set on table. make sure you shift it to a parent element and not table.
-
remjx about 4 years"If your sticky element is the only child of the container, the container is the exact same size, and there's no room to scroll." GOLD!!! Thank you!
-
mehov about 4 yearsUnsure why this got downvoted by someone, but this was my case exactly: I added
float:left
to the parent element (without float it's height was 0) andposition:sticky
worked. -
narasimha sriharsha Kanduri about 4 yearshow cme you guys know this kind of stuff. are we missing something(not reading documentation, or code or what?) or it comes naturally by experience . Please suggest
-
Ben Philipp almost 4 yearsI just found out through experimenting that the sticky won't scroll outside its parent element. It doesn't so much matter how many other siblings it has, but its parent can't, apparently, be a navbar container but has to be something on tle level of a page/content container :( This is ridiculous
-
Ben Philipp almost 4 yearsOh! This seems to be a work-around for the case that a parent element has a limited height (e.g. a navbar container or something, as opposed to a whole body or page container) - it seems that stickies don't like to scroll outside their parents (which is utterly ridiculous!) Now everybody just needs to be so lucky as to be able to set the parent to
display: unset
, which may not always be immediately viable -
Ben Philipp almost 4 yearsBut Lo! If you can, if you set the limiting parent to
display: unset
, this restriction is lifted! see stackoverflow.com/a/60560997/2902367 Also seems to work withcontents
,initial
(?) andinline
-
Ben Philipp almost 4 yearsAlso seems to work with
contents
,initial
(?) andinline
-
Ben Philipp almost 4 yearsYes! Took me a while to figure this one out. I found it a ridiculous restriction at first, but I suppose there should be at least the option for that. I'd much prefer something like
sticky-limit: parent
,sticky-limit: body
orsticky-limit: nth-parent(3)
... Anyway: If you can, you can lift this limitation by setting the limiting parent todisplay: unset
, as noted in stackoverflow.com/a/60560997/2902367 . Also seems to work withcontents
,initial
(?) andinline
-
vintprox almost 4 yearsOne could as well handle
position: fixed
and appropriate properties with JavaScript. Question doesn't state the need for JavaScript. -
Athlan over 3 yearsI have checked all parents going up and non of them uses overflow attribute. There must be something else.
-
Yangshun Tay over 3 yearsHere is a useful article explaining this behavior in more detail - medium.com/@elad/…
-
Debu Shinobi over 3 yearsHey @ViliusL, Thanks! man. Your comment saved a whole lot of headache and helped a lot more than the answer itself. LOL
-
Javier Dottori about 3 yearsI had a similar problem because the parent had
height; 100vh
and that doesn't work with sticky on child (Chrome 89 - Ubuntu) -
PrasadW about 3 yearsOk so after I played around a bit, I find that only your tables immediate parent only should not have an "overflow" attribute, doesn't matter what it's set to, it should not be present. So, I would say you don't have to go up the DOM tree. But otherwise this answer is crucial to understanding how the sticky works.
-
mix3d about 3 yearsAlso curious if this can be backed up somehow
-
aruno almost 3 yearsNote: Unfortunately
overflow-x
also breaks even for a vertically positioned sticky item. -
blid almost 3 yearsThe example of
position: sticky
in the API is literally an element inside aoverflow: auto
-container. Isn't the whole point ofposition: sticky
to live inside scroll containers? This answer confuses me so much. -
Michael Czechowski over 2 yearsThe question was "how does it work" and not "how does it not work" :) Please, add some solutions or spare them out. You can also add comments to the questions, instead of adding "Answers" which are not answers.
-
Mahdi-Jafaree over 2 yearsYes! this solved my problem. can you tell me the reason?
-
Sumit Wadhwa over 2 yearsit works. But why does it work.?
-
Sumit Wadhwa over 2 yearsoverflow on parent wouldn't matter if you just add
display: initial
to it (the parent). -
Sumit Wadhwa over 2 years
display: initial;
also works. -
David Douglas over 2 years
align-items: flex-start;
in the parent flex container was the thing I was missing -
graup over 2 years"Parent also should not have overflow property," this is not quite correct. More precisely, only the scrollable parent should have an overflow property and no other elements in between.
-
b4tch about 2 yearsAssuming this "works" because the
overflow
property does not apply to inline elements (which for some reason is whatdiv
elements are when set todisplay: initial
(orunset
) -
Razvan-Catalin Olaru about 2 yearsI think @blid gave a good example as to how position sticky actually REQUIRES a parent element that has a known height and is scrollable, like demonstrated in this Stackblitz: stackblitz.com/edit/position-sticky-example?file=index.html . You can see that it works and when you try to remove
height: 100%;
andoverflow: auto;
from.page-content
it will still work. It won't work if you try to remove instead only the height property. Tested in Chrome version 100.0.4896.127 on Mac -
Benzara Tahar about 2 yearsI find the "how does it not work" question to be more legit in this case, since the "how it does work" is pretty simple and you need to find out in most cases what went wrong, thus that question
-
devmrh almost 2 yearsYou may have to inspect the DOM tree higher than you expect =). +1 :D