How does the "position: sticky;" property work?

501,516

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 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;
}
<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 level div 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 using bottom.

  • 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.
Share:
501,516
Harleyoc1
Author by

Harleyoc1

Updated on February 03, 2022

Comments

  • Harleyoc1
    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
    ViliusL about 6 years
    In general, this answer is not enough to sticky to work, parent also should not have overflow property.
  • Marvin
    Marvin about 6 years
    Thank 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
    aruno almost 6 years
    @Sergey I'm not absolutely clear which of display: block and position: relative triggers the correct behavior in Safari - did it seem that only display: block was needed? Of course it probably doesn't hurt to have both specified
  • Sergey
    Sergey almost 6 years
    Just display: block was enough
  • danday74
    danday74 almost 6 years
    This 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
    danday74 almost 6 years
    This is also the right answer but also see my answer which helps to easily identify the parent causing the problem.
  • Cedric Ipkiss
    Cedric Ipkiss almost 6 years
    I missed the "s" in your "parent elements"
  • Mariusz Pawelski
    Mariusz Pawelski almost 6 years
    I 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 with overflow:hidden style that caused the problem. I wish I read this answer more carefully.
  • Mariusz Pawelski
    Mariusz Pawelski almost 6 years
    You should check not only closest parent container but all parent elements.
  • danday74
    danday74 almost 6 years
    yes, see my answer to do exactly that and check ALL parents very quickly
  • David Brower
    David Brower over 5 years
    That was very helpful. I was able to quickly identify the parent element that had a value of overflow: invisible.
  • lmiller1990
    lmiller1990 over 5 years
    The 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
    magikMaker over 5 years
    If 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
    AJMansfield over 5 years
    Do you have a source for this, or any other information about how to work around this?
  • Mariusz Pawelski
    Mariusz Pawelski over 5 years
    You 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
    saglamcem over 5 years
    Thank 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
    code4j about 5 years
    I 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
    J.McLaren almost 5 years
    Very 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
    brandonjp almost 5 years
    If 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
    Rahul Singh over 4 years
    Life saver, really simple description to the problem , but is there a work around to this just curious
  • Benjamin Intal
    Benjamin Intal over 4 years
    Even overflow-x: hidden will stop the element from sticking. However, overflow-x: hidden in the body tag will work.
  • Ángel Jiménez
    Á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
    MaciejLisCK over 4 years
    display: flex makes the thing
  • Piotr Szlagura
    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
    Rohan Shenoy over 4 years
    sticky position should not have a parent with overflow property, also sticky is not supported in some web browsers
  • Rohan Shenoy
    Rohan Shenoy over 4 years
    If 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
    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
    mehov about 4 years
    Unsure 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) and position:sticky worked.
  • narasimha sriharsha Kanduri
    narasimha sriharsha Kanduri about 4 years
    how 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
    Ben Philipp almost 4 years
    I 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
    Ben Philipp almost 4 years
    Oh! 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
    Ben Philipp almost 4 years
    But 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 with contents, initial(?) and inline
  • Ben Philipp
    Ben Philipp almost 4 years
    Also seems to work with contents, initial(?) and inline
  • Ben Philipp
    Ben Philipp almost 4 years
    Yes! 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 or sticky-limit: nth-parent(3)... Anyway: If you can, you can lift this limitation by setting the limiting parent to display: unset, as noted in stackoverflow.com/a/60560997/2902367 . Also seems to work with contents, initial(?) and inline
  • vintprox
    vintprox almost 4 years
    One could as well handle position: fixed and appropriate properties with JavaScript. Question doesn't state the need for JavaScript.
  • Athlan
    Athlan over 3 years
    I have checked all parents going up and non of them uses overflow attribute. There must be something else.
  • Yangshun Tay
    Yangshun Tay over 3 years
    Here is a useful article explaining this behavior in more detail - medium.com/@elad/…
  • Debu Shinobi
    Debu Shinobi over 3 years
    Hey @ViliusL, Thanks! man. Your comment saved a whole lot of headache and helped a lot more than the answer itself. LOL
  • Javier Dottori
    Javier Dottori about 3 years
    I had a similar problem because the parent had height; 100vh and that doesn't work with sticky on child (Chrome 89 - Ubuntu)
  • PrasadW
    PrasadW about 3 years
    Ok 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
    mix3d about 3 years
    Also curious if this can be backed up somehow
  • aruno
    aruno almost 3 years
    Note: Unfortunately overflow-x also breaks even for a vertically positioned sticky item.
  • blid
    blid almost 3 years
    The example of position: sticky in the API is literally an element inside a overflow: auto-container. Isn't the whole point of position: sticky to live inside scroll containers? This answer confuses me so much.
  • Michael Czechowski
    Michael Czechowski over 2 years
    The 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
    Mahdi-Jafaree over 2 years
    Yes! this solved my problem. can you tell me the reason?
  • Sumit Wadhwa
    Sumit Wadhwa over 2 years
    it works. But why does it work.?
  • Sumit Wadhwa
    Sumit Wadhwa over 2 years
    overflow on parent wouldn't matter if you just add display: initial to it (the parent).
  • Sumit Wadhwa
    Sumit Wadhwa over 2 years
    display: initial; also works.
  • David Douglas
    David Douglas over 2 years
    align-items: flex-start; in the parent flex container was the thing I was missing
  • graup
    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
    b4tch about 2 years
    Assuming this "works" because the overflow property does not apply to inline elements (which for some reason is what div elements are when set to display: initial (or unset)
  • Razvan-Catalin Olaru
    Razvan-Catalin Olaru about 2 years
    I 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%; and overflow: 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
    Benzara Tahar about 2 years
    I 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
    devmrh almost 2 years
    You may have to inspect the DOM tree higher than you expect =). +1 :D