52

I have an element that I am making sticky with position sticky:

#header {
    position: sticky;
    width: 100vw;
    top: 0;
}
<app-header id="header"></app-header>

And that works fine, but I realised that if I use:

body {
  overflow-x: hidden;
}

That breaks sticky, and I need to set body overflow-x to hidden, how can I fix that, with only CSS solution, no JS solutions?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Leff
  • 1,968
  • 24
  • 97
  • 201
  • 3
    Note that sticky, by specification, will not work inside element with overflow: hidden or auto. Also, `position: sticky` is an experimental API and should not be used in production, just a heads up (in case you aren't already aware of that) **see:** https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky – UncaughtTypeError Nov 03 '17 at 12:21
  • Yes, wasn't aware of that, thank you for pointing this out – Leff Nov 03 '17 at 12:36
  • The only way I was able to fix this was to unset that `overflow-x` with something like `overflow-x: initial;`. A year later and `position: sticky` is no longer experimental and works cross browser (minus good ol' IE) :) – protoEvangelion Jan 09 '19 at 17:55

11 Answers11

44

Setting the overflow-x property to value clip helped me achieve position sticky and prevent scrolling.

Here is more explanation in this article

Yurii Kuzemko
  • 671
  • 5
  • 10
22

UPDATE:

This has been successfully tested on Safari v12.0.2, Firefox v64.0, and Chrome v71.0.3578.98

Added position: -webkit-sticky; for Safari.


Unfortunately the spec is not too clear about the implications of overflow-x: hidden; on position sticky, but there is a way to fix this. Thankfully there is an issue to hopefully fix this: https://github.com/w3c/csswg-drafts/issues/865.

The simple solution is to remove or unset overflow-x: hidden; from every ancestor of the element you want to have position: sticky;. Then you can have overflow-x: hidden; on the body and it will work!

Also double check that you don't have overflow set on both the body and html tags which I posted about more in depth here: https://stackoverflow.com/a/54116725/6502003

Here is a pen if you want to play around with it: https://codepen.io/RyanGarant/pen/REYPaJ

/* 
  Try commenting out overflow on body style and uncommenting
  overflow on .overflow-x-hidden class and you will see 
  position sticky stop working!  
*/

body {
  overflow-x: hidden;
}

.overflow-x-hidden {
/*   overflow-x: hidden; */
  border: 1px solid blue;
}

h1 {
  background: palevioletred;
  color: #fff;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

.tall {
  background: linear-gradient(to bottom, paleturquoise, white);
  height: 300vh;
  width: 100%;
}
<div class="overflow-x-hidden">
  <h1>I want to be sticky!</h1>
  
  <div class="tall"></div>
</div>
protoEvangelion
  • 4,355
  • 2
  • 29
  • 38
  • 3
    Currently (Firefox 78, Chrome 84) this doesn't seem to work any more. Verified by toggling the `overflow-x:hidden` property on the body element. – Ben Carp Jul 21 '20 at 19:41
  • 5
    "The simple solution is to remove or unset overflow-x: hidden; from every ancestor of the element you want to have position: sticky;" Isn't the `body` tag an ancestor of every element anyway? – devamat Feb 05 '21 at 02:39
  • Your "both the body and html tags" url links to itself. Is there any more info somewhere becasue I need to have it on both html and body because of https://stackoverflow.com/questions/3047337 – FINDarkside Oct 07 '22 at 06:11
  • Did you see this link I added @FINDarkside? https://stackoverflow.com/a/54116725/6502003 – protoEvangelion Oct 11 '22 at 01:29
  • This does now work. Note that Safari's `html {overflow-x:hidden; }` behavior seems to be different inside an – Már Örlygsson Jan 26 '23 at 11:54
  • OH MY STARS this freakin fixed so many projects > Also double check that you **don't have overflow set on both the body and html tags** – jfudman Jul 19 '23 at 19:49
5

The sticky doesn't work inside element with overflow: hidden or auto. Refer to this https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky

A workaround can be working with this library

Renzo Calla
  • 7,486
  • 2
  • 22
  • 37
  • Hmmm, the link you advise the OP to refer to doesn't mention anything about how `position: sticky` will not work inside elements with `overflow: hidden/auto` - the MDN document does, but not at the section you've anchored to. Also, when you repeat what has already been established in the comments it looks like you are just trying to reputation farm. – Just use Flex Nov 03 '17 at 12:43
  • Your are right it does but in the upper definition I just corrected, thanks – Renzo Calla Nov 03 '17 at 12:46
  • Same problem facing how to run both body {overflow-x: hidden} and child is position: sticky – Inderjeet Mar 15 '18 at 10:39
5
body, html { overflow-x: clip; } 

This works well with position: sticky

Im using transform: translateX(-100px) for load animations and overflow-x:clip prevents expanding the viewport and showing horizontal scrollbar..

bpfrd
  • 945
  • 3
  • 11
Barty Bart
  • 61
  • 1
  • 4
2

I think you have to fix :

body {
  overflow-x: clip;
}

I hope this works .

Ikramzv
  • 31
  • 4
1

Not sure if this helps, but I struggled with this for mobile (where the page would "overflow" and scroll past the boundaries) for so long and finally fixed it.

I tried the body {overflow: hidden;} solution, but it would disable my sticky elements. So I then created a <div class="container"> that contained all of my other divs of the page inside the <body>, and that seemed to do it!

1

I just want to add this here if anyone runs into a similar issue. position:sticky was not working for me because we are applying overflow-y:scroll on tablet/mobile screen resolutions. overflow: and position:sticky do not work together. This article explains the 2 workarounds to fix it. I went with setting a fixed height on the table and then hiding the vertical scroll bar with

::-webkit-scrollbar {
  width: 0;  
  background: transparent;  
}::-webkit-scrollbar {
  width: 0;  
  background: transparent;  /* 
}
jawn
  • 851
  • 7
  • 10
1

This is how I make it work for me:
Create a parent wrapper/container element that should contains all your other page elements, and give it a fixed height (mostly height: 100vh) with overflow-y: auto and overflow-x: hidden as it's described in the code/snippet below ..
and make the body overflow: hidden to avoid any extra scroll bars ..

This solution may comes with some consequences/restrictions, for example:
if you're used to use the CSS scroll-behavior property in the html element you will need then to move it to the #page_wrapper element instead ..

body {
  margin: 0;
  overflow: hidden;
}

.page-wrapper {
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
}

.sticky {
  position: sticky;
  left: 0;
  padding: 4px;
  text-align: center;
  background-color: forestgreen;
}

.sticky-header {
  top: 0;
}
.sticky-footer {
  bottom: 0;
}

.long-content {
  padding: 2rem;
  min-height: 100vh;
}

.break-overflow-x {
  min-width: 200vw;
  padding: 2rem;
  background-color: red;
}
<body>
  <div class="page-wrapper">
    <div class="sticky sticky-header"><button>Sticky Header</button></div>
    <div class="long-content">Long vertical content</div>
    <div class="break-overflow-x">Long horizontal content</div>
    <div class="long-content">Long vertical content</div>
    <div class="sticky sticky-footer"><button>Sticky Footer</button></div>
  </div>
</body>
Az.Youness
  • 2,167
  • 1
  • 24
  • 33
0

My solution is to replace overflow: hidden with this:

display: flow-root;
overflow: clip;
Andrew
  • 91
  • 5
-1

can you try with the following...

#one { position: sticky; top: 10px; }* {
  box-sizing: border-box;
}
body{overflow-x: hidden;}
dl > div {
  background: #FFF;
  padding: 24px 0 0 0;
}

dt {
  background: #B8C1C8;
  border-bottom: 1px solid #989EA4;
  border-top: 1px solid #717D85;
  color: #FFF;
  font: bold 18px/21px Helvetica, Arial, sans-serif;
  margin: 0;
  padding: 2px 0 0 12px;
  position: -webkit-sticky;
  position: sticky;
  top: -1px;
}

dd {
  font: bold 20px/45px Helvetica, Arial, sans-serif;
  margin: 0;
  padding: 0 0 0 12px;
  white-space: nowrap;
}

dd + dd {
  border-top: 1px solid #CCC;
}
<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/position -->

<dl>
  <div>
    <dt>A</dt>
    <dd>Andrew W.K.</dd>
    <dd>Apparat</dd>
    <dd>Arcade Fire</dd>
    <dd>At The Drive-In</dd>
    <dd>Aziz Ansari</dd>
  </div>
  <div>
    <dt>C</dt>
    <dd>Chromeo</dd>
    <dd>Common</dd>
    <dd>Converge</dd>
    <dd>Crystal Castles</dd>
    <dd>Cursive</dd>
  </div>
  <div>
    <dt>E</dt>
    <dd>Explosions In The Sky</dd>
  </div>
  <div>
    <dt>T</dt>
    <dd>Ted Leo &amp; The Pharmacists</dd>
    <dd>T-Pain</dd>
    <dd>Thrice</dd>
    <dd>TV On The Radio</dd>
    <dd>Two Gallants</dd>
  </div>
</dl>
  
-2

For those who have the same issue. I added overflow: hidden to the div below the header holding all the contents.

 <div class="header" style="position: sticky;">
        //navs
 </div>
 <div class="content-div" style="overflow: hidden;">
       //contents 
 </div>