position:sticky does not leave parent
Asked Answered
N

7

37

How can I make an element sticky, so it stays at the top of the viewport? I want the element to remain sticky even if it leaves it's container.

I tried this:

.child-sticky {
  height: 200px;
  background: #333366;
  position:sticky;
  top:20px;
  color:#ffffff;
}

.parent {
  background: #555599;
}

.child {
  background-color: #8888bb;
}

.page {
  height: 3000px;
  background: #999999;
  width: 500px;
  margin: 0 auto;
}

div {
  padding: 20px;
}
<div class="page">
  <div class="parent">
    <div class="child"><p>...<br>...<br>...<br>...<br>...</p></div>
    <div class="child-sticky">
      <p>i want to be sticky, even when I'm outside my parent.</p>
    </div>
    <div class="child"><p>...<br>...<br>...<br>...<br>...</p></div>
    <div class="child"><p>...<br>...<br>...<br>...<br>...</p></div>
  </div>
</div>
Nuggar answered 24/10, 2017 at 14:23 Comment(1)
I think they know it's not ideal. If I'm interpreting this issue correctly, the ideal would be that the sticky would be relative to the nearest scrolling ancestor, but complications with the CSS Object Model prevent that.Astrogate
M
26

Sticky works that way, it will remain sticky relative to its parent. You need to use fixed. Check this codepen

Melisamelisande answered 24/10, 2017 at 14:26 Comment(2)
i was hoping for a css-only solution to do this, but as it's just a couple of lines of js this looks like the best solution at the momentNuggar
Damn, that's a letdown.Oquendo
N
22

Already 7 months ago, but I found a CSS only solution if the element you want to be sticky is the last one of its parent, its very simple: Just give the parent element position: sticky; and also give it top: -xx;, depending on the height of the elements before the last one.

#parent {
  position: -webkit-sticky;
  position: sticky;
  top: -3em;
}

#some_content {
  height: 3em;
}

#sticky {
  background-color: red;
}

#space {
  height: 200vh;
}
<div id="parent">
  <div id="some_content">Some Content</div>
  <div id="sticky">Sticky div</div>
</div>

<div id="space"></div>
<p>Scroll here</p>
Nonego answered 16/6, 2018 at 12:11 Comment(0)
W
4

There is a little trick you can try. In some cases it will break your layout and in others it won't. In my case, I have a header with some buttons but when I scroll down, I want to have access to some action buttons which are inside that one-line header. The only change is to set the display property of your parent to be inline.

.parent {
  display: inline;
}
Winnick answered 9/11, 2022 at 10:38 Comment(1)
Very very cool, thank you, can you tell me the reason? Because I want to understand why inline solve the problem? What do you do in the father element?Inclinometer
C
3

This is how position: sticky is intended to work. If you need it to also work outside the parent than you have to change the HTML structure.

See also the official definition: https://www.w3.org/TR/css-position-3/#sticky-pos

Catgut answered 24/10, 2017 at 14:29 Comment(2)
Nice concept as long as you can control the HTML markup. Enter WordPress and other low-code "solutions" and you have parent containers with non-semantic markup like `<div class="wp-block-template-part". Thanks for making it clear though.Pyrrhonism
Very often the header that should be sticky is inside another element (e.g. a cover block) and should stretch it with its own dimensions (the header dimensions are also dynamic). Position sticky fixes the issue - until the header should leave the parent element, which would not work.Danzig
G
1

Based on https://mcmap.net/q/416053/-position-sticky-does-not-leave-parent and https://mcmap.net/q/364104/-make-a-nav-bar-stick's code, here's an combined solution that does not require hard-coded height.

$(document).ready(function() {
    // Get the current top location of the nav bar.
    var stickyNavTop = $('nav').offset().top;
  
    // Set the header's height to its current height in CSS
    // If we don't do this, the content will jump suddenly when passing through stickyNavTop.
    $('header').height($('header').height());
  
    $(window).scroll(function(){
        if ($(window).scrollTop() >= stickyNavTop) {
            $('nav').addClass('fixed-header');
        } else {
            $('nav').removeClass('fixed-header');
        }
    });
});
body { margin: 0px; padding: 0px; }

nav {
    width: 100%;
    background-color: red;
}

.fixed-header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 10;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<header>
  <div>
    <h1 style="padding-bottom: 50px; background-color: blue;">
    Hello World!
    </h1>
  </div>
  <nav>
    A nav bar here!
  </nav>
</header>

<main style="height: 1000px;">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</main>
Glaucoma answered 25/12, 2018 at 2:48 Comment(0)
P
1

Like mentioned, sticky works that way. However there is a CSS hack that you can play around with.

Disclaimer

This is an ugly hack and will probably create a lot of problems with the following content. So I would not recommend it for most use cases. Having said that...

Negative margin hack

There is a hack you could play around with including negative margin. If you extend the height of the container and then give it some negative bottom margin, it could at least "visually leave" the container.

.child-sticky {
  height: 200px;
  background: #333366;
  position:sticky;
  top:20px;
  color:#ffffff;
}

.parent {
  height: 1250px;
  background: #555599;
  margin-bottom: -500px;
}
.following-content {
  background: red;
  height:500px;
}

.child {
  background-color: #8888bb;
}

.page {
  height: 3000px;
  background: #999999;
  width: 500px;
  margin: 0 auto;
}

div {
  padding: 20px;
}
<div class="page">
  <div class="parent">
    <div class="child"><p>...<br>...<br>...<br>...<br>...</p></div>
    <div class="child-sticky">
      <p>i want to be sticky, even when I'm outside my parent.</p>
    </div>
    <div class="child"><p>...<br>...<br>...<br>...<br>...</p></div>
    <div class="child"><p>...<br>...<br>...<br>...<br>...</p></div>
  </div>
  <div class="following-content">
  </div>
</div>
Prisca answered 12/4, 2020 at 19:35 Comment(0)
M
0

You might also able to use display: contents on your parent element to ignore it regarding sticky behaviour, but it will ignore all other properties too (layout properties, background colors etc.)

Motile answered 26/10, 2023 at 15:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.