Custom scroll next js [duplicate] - css

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>Contact</li>
<li>About</li>
<li>Products</li>
<li>Home</li>
</ul>
</nav>
</main>

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.

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>Contact</li>
<li>About</li>
<li>Products</li>
<li>Home</li>
</ul>
</nav>

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.

Incase you came across this and your sticky is not working - try setting the parent to:
display: unset
Worked for me

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.

This is a continuation of the answers from MarsAndBack and Miftah Mizwar.
Their answers are correct. However, it is difficult to identify the problem ancestor(s).
To make this very simple, simply run this jQuery script in your browser console and it will tell you the value of the overflow property on every ancestor.
$('.your-sticky-element').parents().filter(function() {
console.log($(this));
console.log($(this).css('overflow'));
return $(this).css('overflow') === 'hidden';
});
Where an ancestor does not have overflow: visible change its CSS so that it does!
Also, as stated elsewhere, make sure your sticky element has this in the CSS:
.your-sticky-element {
position: sticky;
top: 0;
}

I had to use the following CSS to get it working:
.parent {
display: flex;
justify-content: space-around;
align-items: flex-start;
overflow: visible;
}
.sticky {
position: sticky;
position: -webkit-sticky;
top: 0;
}
If above dosen't work then...
Go through all ancestors and make sure none of these elements have overflow: hidden. You have to change this to overflow: visible

Sticky Position will not work if your parent is using display flex. As I read this in one solution
Since flex box elements default to stretch, all the elements are the same height, which can't be scrolled against.
So if you are using display: flex; on parent then you will have to add this to sticky element align-self: flex-start; and also set height to auto height: auto;
This is how sticky element class look like
.stick-ontop {
position: -webkit-sticky !important; // for safari
position: sticky !important;
top: 0;
align-self: flex-start;
height: auto;
}

Another very common scenario where position: sticky might not be working is if it's parent has display: flex or display: grid.
What happens in this case is the sticky position is working but you can't see it bcoz the element is stretched completely. Try reducing it's height using align-self: baseline and you'll see the effect.

Attack this Q from other direction.
Imagine this is a game Find the nearest scrolling ancestor.
<!-- sticky not working -->
<h1 style="position: sticky; top:0;">Hello World</h1>
Questions:
1/3: The sticky node? <h1>.
2/3: The ancestor? <body>.
3/3: <body> scrolling ? FALSE => "No effect".
Fix: "Sticky is working" (<body> scrolling ? TRUE).
body{
min-height: 300vh;
}
<!-- sticky working -->
<h1 style="position: sticky; top: 0;">Hello World</h1>
With this in mind - here are some "hello world" "famous" scenarios of "not working" sticky :) Most cases relate to one or many of these cases.
Case 1: Missing "top" (Easy to fix):
Not working:
/* not working example */
aside{
position: sticky;
background: lightgray;
}
main{
height: 200vh;
}
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
Fix (Add top):
aside{
position: sticky;
top: 0;
}
main{
height: 200vh;
}
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
Case 2: Sticky node & overflow (Easy to fix):
I "destroy" the sticky by adding #extra-wrapper with overflow setting auto -or- hidden -or- visible - but without any clipped content.
"The problem" now the nearest scrolling ancestor (#extra-wrapper) "without" any scrolling (No scrollbar dragging option == "no scrolling ancestor").
Not working:
/* not working example */
#overflow-wrapper{
overflow: scroll;
}
aside{
position: sticky;
background: lightgray;
top: 0px;
}
main{
height: 200vh;
}
<div id="overflow-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
Fix - Clip the content (Now their is "nearest scrolling ancestor").
Working:
/* not working example */
#overflow-wrapper{
overflow: scroll;
max-height: 60vh; /* clip the content */
}
aside{
position: sticky;
background: lightgray;
top: 0px;
}
main{
height: 200vh;
}
<div id="overflow-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
Case 3: Sticky related to "wrong / not scrolling" node (Tricky to fix)
Again, Sticky offset relative to its nearest scrolling ancestor.
I "destroy" the sticky by adding #extra-wrapper to the sticky element. Why it is not working? Now the height of #extra-wrapper == height aside content (box model) == "no scrolling ancestor" == "no effect".
Not working:
/* not working example */
aside{
position: sticky;
top: 0;
background: lightgray;
}
main{
height: 200vh;
}
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
This is what really "happens" (I added height to #extra-wrapper):
#extra-wrapper{
background: lightgray;
height: 40vh;
}
aside{
position: sticky;
top: 0;
}
main{
height: 200vh;
}
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
FIX:
change the sticky node:
#extra-wrapper{
position: sticky;
top: 0;
}
aside{
}
#layout{
displ
}
main{
height: 200vh;
}
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
Case 4: display: flexbox/Grid layout - even cols by deafult (Tricky to fix)
You create flex/grid layout & set one of the cols to be sticky. By default the cols height is even = The height of the "nearest ancestor" (wrapper) == Cols height = no scroll effect.
Not working:
#extra-wrapper{
position: sticky;
top: 0;
border: 1px solid red;
}
aside{
}
#layout{
display: flex;
}
main{
height: 200vh;
}
<div id="layout">
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
FIX: Set the sticky aside max-height to be 90vh for example (Now the cols height is not even).
Working:
#extra-wrapper{
position: sticky;
top: 0;
border: 1px solid red;
max-height: 90vh;
}
aside{
}
#layout{
display: flex;
}
main{
height: 200vh;
}
<div id="layout">
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>

It seems like that the navbar to be stickied shouldn't be inside any div or section with other content. None of the solution were working for me until I took the navbar out of the div which the navbar shared with another topbar .I previously had topbar and navbar wrapped with a common div.

I know this seems to be already answered, but I ran into a specific case, and I feel most answers miss the point.
The overflow:hidden answers cover 90% of the cases. That's more or less the "sticky nav" scenario.
But the sticky behavior is best used within the height of a container. Think of a newsletter form in the right column of your website that scrolls down with the page.
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.
Your container needs to be the height you expect your element to scroll within. Which in my "right column" scenario is the height of the left column.
The best way to achieve this is to use display:table-cell on the columns. If you can't, and are stuck with float:right and such like I was, you'll have to either guess the left column height of compute it with Javascript.

I know it's too late. But I found a solution even if you are using overflow or display:flex in parent elements sticky will work.
steps:
Create a parent element for the element you want to set sticky (Get sure that the created element is relative to body or to full-width & full-height parent).
Add the following styles to the parent element:
{
position: absolute;
height: 100vmax;
}
For the sticky element, get sure to add z-index that is higher than all elements in the page.
That's it! Now it must work. Regards

1.Position sticky will most probably not work if overflow is set to hidden, scroll, or auto on any of the parents of the element.
2.Position sticky may not work correctly if any parent element has a set height.

Funny moment that wasn't obvious for me: at least in Chrome 70 position: sticky is not applied if you've set it using DevTools.

I know this is an old post. But if there's someone like me that just recently started messing around with position: sticky this can be useful.
In my case i was using position: sticky as a grid-item. It was not working and the problem was an overflow-x: hidden on the html element. As soon as i removed that property it worked fine. Having overflow-x: hidden on the body element seemed to work tho, no idea why yet.

from my comment:
position:sticky needs a coordonate to tel where to stick
nav {
position: sticky;
top: 0;
}
.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;
}
body {
height: 200vh;
}
<nav>
<ul align="left">
<li>Contact</li>
<li>About</li>
<li>Products</li>
<li>Home</li>
</ul>
</nav>
There is polyfill to use for other browsers than FF and Chrome . This is an experimental rules that can be implemented or not at any time through browsers. Chrome add it a couple of years ago and then dropped it, it seems back ... but for how long ?
The closest would be position:relative + coordonates updated while scrolling once reached the sticky point, if you want to turn this into a javascript script

two answer here:
remove overflow property from body tag
set height: 100% to the body to fix the problem with overflow-y: auto
min-height: 100% not-working instead of height: 100%

I believe this article say a lot about how sticky works
How CSS Position Sticky Really Works!
CSS position sticky has two main parts, sticky item & sticky container.
Sticky Item — is the element that we defined with the position: sticky styles. The element will float when the viewport position
matches the position definition, for example: top: 0px .
Sticky Container —is the HTML element which wraps the sticky item. This is the maximum area that the sticky item can float in.
When you define an element with position: sticky you’re automatically
defining the parent element as a sticky container!

z-index is also very important. Sometimes it will work but you just won't see it. Try setting it to some very high number just to be sure. Also don't always put top: 0 but try something higher in case it's hidden somewhere (under a toolbar).

The real behavior of a sticky element is:
First it is relative for a while
then it is fixed for a while
finally, it disappears from the view
A stickily positioned element is treated as relatively positioned until its containing block crosses a specified threshold (such as setting top to value other than auto) within its flow root (or the container it scrolls within), at which point it is treated as "stuck" until meeting the opposite edge of its containing block.
The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left. The offset does not affect the position of any other elements.
This value always creates a new stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow is hidden, scroll, auto, or overlay), even if that ancestor isn't the nearest actually scrolling ancestor.
This example will help you understand:
code https://codepen.io/darylljann/pen/PpjwPM

Using the strategy from this blog (https://www.designcise.com/web/tutorial/how-to-fix-issues-with-css-position-sticky-not-working) I came up with an option for those that can't have control over all components in the page
I'm using Angular and in the ngOnInit method I run this code to change the visible propertys of parents to visible
/**
* position: sticky
* only works if all parent components are visibile
*/
let parent = document.querySelector('.sticky').parentElement;
while (parent) {
const hasOverflow = getComputedStyle(parent).overflow;
if (hasOverflow !== 'visible') {
parent.style.overflow = 'visible';
// console.log(hasOverflow, parent);
}
parent = parent.parentElement;
}

Watch out for empty grid areas!
In my case I had something like this:
.cart-areas {
grid-template-columns: 2fr 0.2fr 1.5fr;
grid-template-areas:
'order . summary'
'order . .'
'order . .';
}
I wanted that summary grid item to be sticky when the user completes the checkout form. It didn't work because of those empty grid items (marked with .).
The solution was to delete those empty items like so:
.cart-areas {
grid-template-columns: 2fr 0.2fr 1.5fr;
grid-template-areas:
'order . summary'
'order . summary'
'order . summary';
}

if danday74's fix doesn't work, check that the parent element has a height.
In my case I had two childs, one floating left and one floating right.
I wanted the right floating one to become sticky but had to add a <div style="clear: both;"></div> at the end of the parent, to give it height.

I used a JS solution. It works in Firefox and Chrome. Any problems, let me know.
html
<body>
<header id="header">
<h1>Extra-Long Page Heading That Wraps</h1>
<nav id="nav">
<ul>
<li>Home</li>
<li>Page 2</li>
<li>Page 3</li>
</ul>
</nav>
</header>
<main>
<p><!-- ridiculously long content --></p>
</main>
<footer>
<p>FOOTER CONTENT</p>
</footer>
<script src="navbar.js" type="text/javascript"></script>
</body>
css
nav a {
background: #aaa;
font-size: 1.2rem;
text-decoration: none;
padding: 10px;
}
nav a:hover {
background: #bbb;
}
nav li {
background: #aaa;
padding: 10px 0;
}
nav ul {
background: #aaa;
list-style-type: none;
margin: 0;
padding: 0;
}
#media (min-width: 768px) {
nav ul {
display: flex;
}
}
js
function applyNavbarSticky() {
let header = document.querySelector('body > header:first-child')
let navbar = document.querySelector('nav')
header.style.position = 'sticky'
function setTop() {
let headerHeight = header.clientHeight
let navbarHeight = navbar.clientHeight
let styleTop = navbarHeight - headerHeight
header.style.top = `${styleTop}px`
}
setTop()
window.onresize = function () {
setTop()
}
}

Here's what was tripping ME up... my sticky div was inside another div so that parent div needed some additional content AFTER the sticky div, to make the parent div "tall enough" for the sticky div to "slide over" other content as you scroll down.
So in my case, right after the sticky div, I had to add:
%div{style:"height:600px;"}
(My application has two side-by-side divs, with a "tall" image on the left, and a short data entry form on the right, and I wanted the data entry form to float next to the image as you scroll down, so the form is always on the screen. It would not work until I added the above "extra content" so the sticky div has something to "slide over"

I had the same problem. For me the problem was display: 'none' on big screens (media-query) and display: 'initial' on smartphones. If i removed the display css property and added opacity and pointer events none on desktop everything workedout.

Despite reading this entire page and trying everything but the kitchen sink this simply doesn't work on my mobile device. No sticky action at all -- and that's the only place I need it to work. I have ruled out any special designation or overrides that would stop this from functioning for this screen width. Cleared my mobile formatting code as a test and no change was seen. Works perfectly on Chrome browser on my laptop and not at all on chrome browser on my new S10.

It's TRUE that the overflow needs to be removed or set to initial to make position: sticky works on the child element. I used Material Design in my Angular app and found out that some Material components changed the overflow value. The fix for my scenario is
mat-sidenav-container, mat-sidenav-content {
overflow: initial;
}

One of the most common mistakes when going about the position sticky is:
Browser not supporting it
Not including any top bottom right left properties. Bare in mind that the Browser won't know how to handle that properly if you don't give it enough information. It will be just be statically positioned without it.
I have written about that and more insides on this article. Just putting a reference so I don't repeat myself too much.

Related

Sticky Menu in React [duplicate]

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>Contact</li>
<li>About</li>
<li>Products</li>
<li>Home</li>
</ul>
</nav>
</main>
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.
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>Contact</li>
<li>About</li>
<li>Products</li>
<li>Home</li>
</ul>
</nav>
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.
Incase you came across this and your sticky is not working - try setting the parent to:
display: unset
Worked for me
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.
This is a continuation of the answers from MarsAndBack and Miftah Mizwar.
Their answers are correct. However, it is difficult to identify the problem ancestor(s).
To make this very simple, simply run this jQuery script in your browser console and it will tell you the value of the overflow property on every ancestor.
$('.your-sticky-element').parents().filter(function() {
console.log($(this));
console.log($(this).css('overflow'));
return $(this).css('overflow') === 'hidden';
});
Where an ancestor does not have overflow: visible change its CSS so that it does!
Also, as stated elsewhere, make sure your sticky element has this in the CSS:
.your-sticky-element {
position: sticky;
top: 0;
}
I had to use the following CSS to get it working:
.parent {
display: flex;
justify-content: space-around;
align-items: flex-start;
overflow: visible;
}
.sticky {
position: sticky;
position: -webkit-sticky;
top: 0;
}
If above dosen't work then...
Go through all ancestors and make sure none of these elements have overflow: hidden. You have to change this to overflow: visible
Sticky Position will not work if your parent is using display flex. As I read this in one solution
Since flex box elements default to stretch, all the elements are the same height, which can't be scrolled against.
So if you are using display: flex; on parent then you will have to add this to sticky element align-self: flex-start; and also set height to auto height: auto;
This is how sticky element class look like
.stick-ontop {
position: -webkit-sticky !important; // for safari
position: sticky !important;
top: 0;
align-self: flex-start;
height: auto;
}
Another very common scenario where position: sticky might not be working is if it's parent has display: flex or display: grid.
What happens in this case is the sticky position is working but you can't see it bcoz the element is stretched completely. Try reducing it's height using align-self: baseline and you'll see the effect.
Attack this Q from other direction.
Imagine this is a game Find the nearest scrolling ancestor.
<!-- sticky not working -->
<h1 style="position: sticky; top:0;">Hello World</h1>
Questions:
1/3: The sticky node? <h1>.
2/3: The ancestor? <body>.
3/3: <body> scrolling ? FALSE => "No effect".
Fix: "Sticky is working" (<body> scrolling ? TRUE).
body{
min-height: 300vh;
}
<!-- sticky working -->
<h1 style="position: sticky; top: 0;">Hello World</h1>
With this in mind - here are some "hello world" "famous" scenarios of "not working" sticky :) Most cases relate to one or many of these cases.
Case 1: Missing "top" (Easy to fix):
Not working:
/* not working example */
aside{
position: sticky;
background: lightgray;
}
main{
height: 200vh;
}
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
Fix (Add top):
aside{
position: sticky;
top: 0;
}
main{
height: 200vh;
}
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
Case 2: Sticky node & overflow (Easy to fix):
I "destroy" the sticky by adding #extra-wrapper with overflow setting auto -or- hidden -or- visible - but without any clipped content.
"The problem" now the nearest scrolling ancestor (#extra-wrapper) "without" any scrolling (No scrollbar dragging option == "no scrolling ancestor").
Not working:
/* not working example */
#overflow-wrapper{
overflow: scroll;
}
aside{
position: sticky;
background: lightgray;
top: 0px;
}
main{
height: 200vh;
}
<div id="overflow-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
Fix - Clip the content (Now their is "nearest scrolling ancestor").
Working:
/* not working example */
#overflow-wrapper{
overflow: scroll;
max-height: 60vh; /* clip the content */
}
aside{
position: sticky;
background: lightgray;
top: 0px;
}
main{
height: 200vh;
}
<div id="overflow-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
Case 3: Sticky related to "wrong / not scrolling" node (Tricky to fix)
Again, Sticky offset relative to its nearest scrolling ancestor.
I "destroy" the sticky by adding #extra-wrapper to the sticky element. Why it is not working? Now the height of #extra-wrapper == height aside content (box model) == "no scrolling ancestor" == "no effect".
Not working:
/* not working example */
aside{
position: sticky;
top: 0;
background: lightgray;
}
main{
height: 200vh;
}
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
This is what really "happens" (I added height to #extra-wrapper):
#extra-wrapper{
background: lightgray;
height: 40vh;
}
aside{
position: sticky;
top: 0;
}
main{
height: 200vh;
}
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
FIX:
change the sticky node:
#extra-wrapper{
position: sticky;
top: 0;
}
aside{
}
#layout{
displ
}
main{
height: 200vh;
}
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
Case 4: display: flexbox/Grid layout - even cols by deafult (Tricky to fix)
You create flex/grid layout & set one of the cols to be sticky. By default the cols height is even = The height of the "nearest ancestor" (wrapper) == Cols height = no scroll effect.
Not working:
#extra-wrapper{
position: sticky;
top: 0;
border: 1px solid red;
}
aside{
}
#layout{
display: flex;
}
main{
height: 200vh;
}
<div id="layout">
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
FIX: Set the sticky aside max-height to be 90vh for example (Now the cols height is not even).
Working:
#extra-wrapper{
position: sticky;
top: 0;
border: 1px solid red;
max-height: 90vh;
}
aside{
}
#layout{
display: flex;
}
main{
height: 200vh;
}
<div id="layout">
<div id="extra-wrapper">
<aside>
<h2>sticky Aside</h2>
</aside>
</div>
<main>
<h1>Article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
</p>
</main>
</div>
It seems like that the navbar to be stickied shouldn't be inside any div or section with other content. None of the solution were working for me until I took the navbar out of the div which the navbar shared with another topbar .I previously had topbar and navbar wrapped with a common div.
I know this seems to be already answered, but I ran into a specific case, and I feel most answers miss the point.
The overflow:hidden answers cover 90% of the cases. That's more or less the "sticky nav" scenario.
But the sticky behavior is best used within the height of a container. Think of a newsletter form in the right column of your website that scrolls down with the page.
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.
Your container needs to be the height you expect your element to scroll within. Which in my "right column" scenario is the height of the left column.
The best way to achieve this is to use display:table-cell on the columns. If you can't, and are stuck with float:right and such like I was, you'll have to either guess the left column height of compute it with Javascript.
I know it's too late. But I found a solution even if you are using overflow or display:flex in parent elements sticky will work.
steps:
Create a parent element for the element you want to set sticky (Get sure that the created element is relative to body or to full-width & full-height parent).
Add the following styles to the parent element:
{
position: absolute;
height: 100vmax;
}
For the sticky element, get sure to add z-index that is higher than all elements in the page.
That's it! Now it must work. Regards
1.Position sticky will most probably not work if overflow is set to hidden, scroll, or auto on any of the parents of the element.
2.Position sticky may not work correctly if any parent element has a set height.
Funny moment that wasn't obvious for me: at least in Chrome 70 position: sticky is not applied if you've set it using DevTools.
I know this is an old post. But if there's someone like me that just recently started messing around with position: sticky this can be useful.
In my case i was using position: sticky as a grid-item. It was not working and the problem was an overflow-x: hidden on the html element. As soon as i removed that property it worked fine. Having overflow-x: hidden on the body element seemed to work tho, no idea why yet.
from my comment:
position:sticky needs a coordonate to tel where to stick
nav {
position: sticky;
top: 0;
}
.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;
}
body {
height: 200vh;
}
<nav>
<ul align="left">
<li>Contact</li>
<li>About</li>
<li>Products</li>
<li>Home</li>
</ul>
</nav>
There is polyfill to use for other browsers than FF and Chrome . This is an experimental rules that can be implemented or not at any time through browsers. Chrome add it a couple of years ago and then dropped it, it seems back ... but for how long ?
The closest would be position:relative + coordonates updated while scrolling once reached the sticky point, if you want to turn this into a javascript script
two answer here:
remove overflow property from body tag
set height: 100% to the body to fix the problem with overflow-y: auto
min-height: 100% not-working instead of height: 100%
I believe this article say a lot about how sticky works
How CSS Position Sticky Really Works!
CSS position sticky has two main parts, sticky item & sticky container.
Sticky Item — is the element that we defined with the position: sticky styles. The element will float when the viewport position
matches the position definition, for example: top: 0px .
Sticky Container —is the HTML element which wraps the sticky item. This is the maximum area that the sticky item can float in.
When you define an element with position: sticky you’re automatically
defining the parent element as a sticky container!
z-index is also very important. Sometimes it will work but you just won't see it. Try setting it to some very high number just to be sure. Also don't always put top: 0 but try something higher in case it's hidden somewhere (under a toolbar).
The real behavior of a sticky element is:
First it is relative for a while
then it is fixed for a while
finally, it disappears from the view
A stickily positioned element is treated as relatively positioned until its containing block crosses a specified threshold (such as setting top to value other than auto) within its flow root (or the container it scrolls within), at which point it is treated as "stuck" until meeting the opposite edge of its containing block.
The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left. The offset does not affect the position of any other elements.
This value always creates a new stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow is hidden, scroll, auto, or overlay), even if that ancestor isn't the nearest actually scrolling ancestor.
This example will help you understand:
code https://codepen.io/darylljann/pen/PpjwPM
Using the strategy from this blog (https://www.designcise.com/web/tutorial/how-to-fix-issues-with-css-position-sticky-not-working) I came up with an option for those that can't have control over all components in the page
I'm using Angular and in the ngOnInit method I run this code to change the visible propertys of parents to visible
/**
* position: sticky
* only works if all parent components are visibile
*/
let parent = document.querySelector('.sticky').parentElement;
while (parent) {
const hasOverflow = getComputedStyle(parent).overflow;
if (hasOverflow !== 'visible') {
parent.style.overflow = 'visible';
// console.log(hasOverflow, parent);
}
parent = parent.parentElement;
}
Watch out for empty grid areas!
In my case I had something like this:
.cart-areas {
grid-template-columns: 2fr 0.2fr 1.5fr;
grid-template-areas:
'order . summary'
'order . .'
'order . .';
}
I wanted that summary grid item to be sticky when the user completes the checkout form. It didn't work because of those empty grid items (marked with .).
The solution was to delete those empty items like so:
.cart-areas {
grid-template-columns: 2fr 0.2fr 1.5fr;
grid-template-areas:
'order . summary'
'order . summary'
'order . summary';
}
if danday74's fix doesn't work, check that the parent element has a height.
In my case I had two childs, one floating left and one floating right.
I wanted the right floating one to become sticky but had to add a <div style="clear: both;"></div> at the end of the parent, to give it height.
I used a JS solution. It works in Firefox and Chrome. Any problems, let me know.
html
<body>
<header id="header">
<h1>Extra-Long Page Heading That Wraps</h1>
<nav id="nav">
<ul>
<li>Home</li>
<li>Page 2</li>
<li>Page 3</li>
</ul>
</nav>
</header>
<main>
<p><!-- ridiculously long content --></p>
</main>
<footer>
<p>FOOTER CONTENT</p>
</footer>
<script src="navbar.js" type="text/javascript"></script>
</body>
css
nav a {
background: #aaa;
font-size: 1.2rem;
text-decoration: none;
padding: 10px;
}
nav a:hover {
background: #bbb;
}
nav li {
background: #aaa;
padding: 10px 0;
}
nav ul {
background: #aaa;
list-style-type: none;
margin: 0;
padding: 0;
}
#media (min-width: 768px) {
nav ul {
display: flex;
}
}
js
function applyNavbarSticky() {
let header = document.querySelector('body > header:first-child')
let navbar = document.querySelector('nav')
header.style.position = 'sticky'
function setTop() {
let headerHeight = header.clientHeight
let navbarHeight = navbar.clientHeight
let styleTop = navbarHeight - headerHeight
header.style.top = `${styleTop}px`
}
setTop()
window.onresize = function () {
setTop()
}
}
Here's what was tripping ME up... my sticky div was inside another div so that parent div needed some additional content AFTER the sticky div, to make the parent div "tall enough" for the sticky div to "slide over" other content as you scroll down.
So in my case, right after the sticky div, I had to add:
%div{style:"height:600px;"}
(My application has two side-by-side divs, with a "tall" image on the left, and a short data entry form on the right, and I wanted the data entry form to float next to the image as you scroll down, so the form is always on the screen. It would not work until I added the above "extra content" so the sticky div has something to "slide over"
I had the same problem. For me the problem was display: 'none' on big screens (media-query) and display: 'initial' on smartphones. If i removed the display css property and added opacity and pointer events none on desktop everything workedout.
Despite reading this entire page and trying everything but the kitchen sink this simply doesn't work on my mobile device. No sticky action at all -- and that's the only place I need it to work. I have ruled out any special designation or overrides that would stop this from functioning for this screen width. Cleared my mobile formatting code as a test and no change was seen. Works perfectly on Chrome browser on my laptop and not at all on chrome browser on my new S10.
It's TRUE that the overflow needs to be removed or set to initial to make position: sticky works on the child element. I used Material Design in my Angular app and found out that some Material components changed the overflow value. The fix for my scenario is
mat-sidenav-container, mat-sidenav-content {
overflow: initial;
}
One of the most common mistakes when going about the position sticky is:
Browser not supporting it
Not including any top bottom right left properties. Bare in mind that the Browser won't know how to handle that properly if you don't give it enough information. It will be just be statically positioned without it.
I have written about that and more insides on this article. Just putting a reference so I don't repeat myself too much.

What exactly is a formatting context?

From MDN:
Everything on a page is part of a formatting context, or an area which has been defined to lay out content in a particular way.
So, according to MDN, formatting context is an area. But it's not clear what they mean by "area". What does "area" mean here?
Also from MDN:
A block formatting context is a part of a visual CSS rendering of a web page. It's the region in which the layout of block boxes occurs and in which floats interact with other elements.
What does being a part of a visual CSS rendering of a web page for an element mean here? It says, it's a region, coinciding with area from the above excerpt. Then it says, BFC is a region—I suppose, they mean a visual region or area of the viewport—where the layout (the visual organization of html elements) happens.
"An element establishes a formatting context" is a very common phrase used throughout many CSS tutorials. display: flow-root establishes a BFC.
.box {
background-color: #ddd;
padding: 1em;
}
.box-1 {
background-color: brown;
width: 7em;
height: 7em;
}
.box-2 {
background-color: cadetblue;
width: 7em;
height: 7em;
}
.para {
background-color: yellowgreen;
}
<div class="box">
<div class="box-1"></div>
<p class="para">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Velit aliquam nisi necessitatibus consequuntur ducimus dolor maxime cumque repellat? Explicabo dolore deleniti aut sequi assumenda dolor ex deserunt, blanditiis itaque eum.
</p>
<div class="box-2"></div>
</div>
Here, the .box element, along with all its contents, is a part of the initial block formatting context—this is what MDN and other tutorials say. Now, if I add the CSS declaration display: flow-root to .box, the .box element will establish a new BFC and then all of its contents will be parts of that newly established BFC, or will participate in that BFC. And, since, according to MDN, BFC is an area or a region of the viewport where the layout "lives" (occurs), the viewport region—perhaps the area of the padding box of the element—that the .box parent element occupies must be the BFC it establishes.
Am I right? Could you make it more clear?
A formatting context is not really an area. It's more a state of a box that defines the set of layout rules that apply to itself and its participant boxes.

Remove unwanted space with position sticky

I'm trying to use position: sticky on a rotated element but I get extra space at the top. Also where the sticky element has to stops (at the end of parent) it goes outside.
Notice I need to have control to choose how many pixels put between sticky element and the left window side.
Check the 2 screenshot to understand the 2 problems, and what I want to achieve.
Problem 1: extra space at top
Problem 2: sticky element goes outside at the end of section
I'm using this code:
HTML
<section class="section">
<h1 class="section__title">STICKY</h1>
<div class="container">
<div class="row">
<div class="col [ col-lg-8 offset-lg-2 ]">
<div class="h4">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam quos illum aperiam officia provident, mollitia at, tempore atque, blanditiis sit optio esse harum officiis voluptas iusto sequi. Magni, reiciendis quidem.
</div>
</div>
</div>
</div>
</section>
CSS
.section__title {
display: inline-block;
transform: rotate(-90deg) translatex(-100%);
transform-origin: 0% 0%;
margin-bottom: 0;
position: sticky;
top: 0;
left: 50px;
}
Here is a Codepen with the entire code: https://codepen.io/anon/pen/KLqJGG
How can I solve this?
Thanks
Problem 1: extra space at top
The stickily positioned element stays in the DOM flow - just like a relative positioned element does. So, hence the space there, which is occupied by the h1.section__title element.
Problem 2: sticky element goes outside at the end of section
It is because, the original height of the h1 element is still considered there, even after rotation.
So, you need to determine the exact width of the sticky header (which then becomes the height of this element after rotation) first and then set this width value for the rotated element's height, as follows:
$section-sticky-header-height: 145px;
.section__title {
display: inline-block;
margin-bottom: 0;
transform-origin: 0% 0%;
position: sticky;
top: 0;
left: 50px;
/* solves problem 1 */
float: left;
/* solves problem 2 */
transform: rotate(-90deg) translatex(-$section-sticky-header-height);
height: $section-sticky-header-height;
}
Codepen: https://codepen.io/anon/pen/arybWZ
Edit:
The problem is that I cannot determine the exact width of the sticky header because the h1 text is variable (the client will insert that text via a CMS). Is there a way to handle this? If possible without Javascript
Got it. You can try this instead, if the height is variable:
<h1 class="h1 mb-0 section__title">
<div class="rotation-outer">
<div class="rotation-inner">
<div class="rotation">
STICKY
</div>
</div>
</div>
</h1>
.section__title {
border: 1px solid; // for demo purpose
position: sticky;
top: 0;
float: left;
.rotation-outer {
display: table;
position: relative;
left: 50px;
.rotation-inner {
padding: 50% 0;
height: 0;
.rotation {
transform-origin: 0 0;
transform: rotate(-90deg) translate(-100%);
margin-top: -50%;
white-space: nowrap;
}
}
}
}
See in action: https://codepen.io/anon/pen/BedREm
There's a very good explanation here for how this works: Rotated elements in CSS that affect their parent's height correctly
Edit 2:
At that link I also discovered the writing-mode: vertical-rl; property (in this answer stackoverflow.com/a/50406895/1252920). Do you think could be a better solution? I applied it in this Codepen: codepen.io/anon/pen/JqyJWK?editors=1100 What do you think?
Yes, another sweet alternative, you can use. :)
Here I changed/optimized it a little bit: https://codepen.io/anon/pen/qGXPPe?editors=1100
However, please note that vertical-lr or vertical-rl is not widely supported. Apparently only on desktop version of Chrome/Firefox/Opera. See here.
So, it's up to you, which one to use. Personally, I wouldn't use writing-mode due to lack of browser support.
Seems to me that your Codepen does not have that padding you mention, therefore I wonder if you removed the default padding and margin of html and body elements.
html, body {
margin: 0;
padding: 0;
}
Let me know if it works or in which browser the padding you mention appears.

My position: sticky element isn't sticky when using flexbox

I was stuck on this for a little bit and thought I'd share this position: sticky + flexbox gotcha:
My sticky div was working fine until I switched my view to a flex box container, and suddenly the div wasn't sticky when it was wrapped in a flexbox parent.
.flexbox-wrapper {
display: flex;
height: 600px;
}
.regular {
background-color: blue;
}
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: red;
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>
JSFiddle showing the problem
Since flex box elements default to stretch, all the elements are the same height, which can't be scrolled against.
Adding align-self: flex-start to the sticky element set the height to auto, which allowed scrolling, and fixed it.
Currently this is supported in all major browsers, but Safari is still behind a -webkit- prefix, and other browsers except for Firefox have some issues with position: sticky tables.
.flexbox-wrapper {
display: flex;
overflow: auto;
height: 200px; /* Not necessary -- for example only */
}
.regular {
background-color: blue; /* Not necessary -- for example only */
height: 600px; /* Not necessary -- for example only */
}
.sticky {
position: -webkit-sticky; /* for Safari */
position: sticky;
top: 0;
align-self: flex-start; /* <-- this is the fix */
background-color: red; /* Not necessary -- for example only */
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>
JSFiddle showing the solution
In my case, one of the parent containers had overflow-x: hidden; applied to it, which will break position: sticky functionality. You'll need to remove that rule.
No parent element should have the above CSS rule applied to it. This condition applies to all parents up to (but not including) the 'body' element.
If you are using flex in the parent element use align-self: flex-start for the element which you want to make sticky.
position: sticky;
align-self: flex-start;
top: 0;
overflow-y: auto;
You can also try adding a child div to the flex item with the contents inside and assign position: sticky; top: 0; to that.
That worked for me for a two column layout where the contents of the first column needed to be sticky and the second column appear scrollable.
For my situation, the align-self: flex-start (or justify-self: flex-start) solution does not work. I need to keep overflow-x: hidden as well since some containers swipe horizontally.
My solution required nested display: flex with overflow-y: auto to get the desired behaviors:
header can adjust height dynamically, which prevents playing with position: absolute or position: fixed
content scrolls vertically, constrained horizontally to the view width
sticky element can be anywhere vertically, sticking to the bottom of the header
other elements can slide horizontally
looks like the SO snippet tool can't render width on child elements to properly to demonstrate the horizontal slide, or maybe there's some other setting on my actual layout that makes it work...
note that a wrapper element that does nothing else is required to allow overflow-x: auto to work correctly in elements under a parent with overflow-x: hidden
body {
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
}
body>header {
background-color: red;
color: white;
padding: 1em;
}
.content {
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
}
article {
position: relative;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.horizontal_slide {
display: flex;
overflow-x: auto;
background-color: lightblue;
padding: .5em;
}
.horizontal_slide>* {
width: 1000px;
}
.toolbar {
position: sticky;
top: 0;
z-index: 10;
background-color: lightgray;
padding: .5em;
display: flex;
}
<header>Fancy header with height adjusting to variable content</header>
<div class="content">
<article class="card">
<h1>One of several cards that flips visibility</h1>
<div class="overflow_x_wrapper">
<div class="horizontal_slide">
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
</div>
<div class="toolbar">Sticky toolbar part-way down the content</div>
<p>Rest of vertically scrollable container with variable number of child containers and other elements</p>
<p>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.</p>
</article>
</div>
Make sure flex-wrapper has assigned a height, and set the overflow value to auto.
Then add "align-self: flex-start;" to the sticky element.
.flexbox-wrapper {
display: flex;
height: 600px; //<- nessary,and don't assign with %
overflow:auto; //<-fix
}
.regular {
background-color: blue;
}
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: red;
align-self: flex-start; // <-fix
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>
I made a makeshift flexbox table and had this problem. To solve it, I simply put the sticky header row outside of the flexbox, just before it.
Global solution/rule from my experience:
Do not put structures with sticky elements directly inside { display : flex } containers.
Instead (for flex layouts) put your tables/divs with sticky elements inside a flex sub-container (e.g. with { flex: 1 1 auto }, but without { display : flex } ), then everything should work as intended.

Difference in Firefox and Chrome's understanding of widths of flex items? [duplicate]

This question already has answers here:
How can I get FF 33.x Flexbox behavior in FF 34.x? [duplicate]
(3 answers)
Closed 7 years ago.
I want to use flexbox to display a three divs in a column layout such that they take up 100% of the width of the parent container. I want these columns to have a exactly 1 line's height. The center div's contents might overflow, so I want it have the nice ellipsis at the end.
I've got a demo for this here: http://jsfiddle.net/f14q15ey/
HTML:
<div class="flex">
<div class="col1">Column 1</div>
<div class="col2">
<span>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Atque eos sed dolorum fugit! Nihil ratione laudantium a cumque vero natus excepturi praesentium error possimus eveniet tempore repudiandae inventore rerum porro?
</span>
</div>
<div class="col3">Column 2</div>
</div>
CSS:
.flex {
display: flex;
}
.col1 {
width: 25%;
flex: 0 0 25%;
}
.col2 {
flex: 1 1 auto;
}
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
display: inline-block;
}
.col3 {
flex: 0 0 25%;
}
It renders just fine in Chrome (41) and Safari. However, in Firefox (37), the inner container expands in width to include the all of the content on the one line and hence pushes the size of the container outside the viewport.
What am I doing wrong? Is this maybe a bug in Firefox's implementation?
Ugh! Adding overflow: hidden to .col2 solves the problem in Firefox. It doesn't make sense to me, but there you go.
On a side note: Three days of raging over this, and I find a fix just minutes after posting here.
I tried something with adding position:absolute ; and max-width:50%. http://codepen.io/cuterajat26/pen/EaBRzd, seems to work.
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 50%;
position:absolute;
display: inline-block;
}

Resources