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;
}
_x000D_
<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>
_x000D_
This question is related to
html
css
position
css-position
sticky
Check if an ancestor element has overflow set (e.g. overflow:hidden
); try toggling it. You may have to go up the DOM tree higher than you expect =).
This may affect your position:sticky
on a descendant element.
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.
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
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;
}
Incase you came across this and your sticky is not working - try setting the parent to:
display: unset
Worked for me
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
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.
from my comment:
position:sticky
needs a coordonate to tel where to stick
nav {_x000D_
position: sticky;_x000D_
top: 0;_x000D_
}_x000D_
_x000D_
.nav-selections {_x000D_
text-transform: uppercase;_x000D_
letter-spacing: 5px;_x000D_
font: 18px "lato", sans-serif;_x000D_
display: inline-block;_x000D_
text-decoration: none;_x000D_
color: white;_x000D_
padding: 18px;_x000D_
float: right;_x000D_
margin-left: 50px;_x000D_
transition: 1.5s;_x000D_
}_x000D_
_x000D_
.nav-selections:hover {_x000D_
transition: 1.5s;_x000D_
color: black;_x000D_
}_x000D_
_x000D_
ul {_x000D_
background-color: #B79b58;_x000D_
overflow: auto;_x000D_
}_x000D_
_x000D_
li {_x000D_
list-style-type: none;_x000D_
}_x000D_
_x000D_
body {_x000D_
height: 200vh;_x000D_
}
_x000D_
<nav>_x000D_
<ul align="left">_x000D_
<li><a href="#/contact" class="nav-selections" style="margin-right:35px;">Contact</a></li>_x000D_
<li><a href="#/about" class="nav-selections">About</a></li>_x000D_
<li><a href="#/products" class="nav-selections">Products</a></li>_x000D_
<li><a href="#" class="nav-selections">Home</a></li>_x000D_
</ul>_x000D_
</nav>
_x000D_
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
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.
The real behavior of a sticky element is:
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:
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;
}
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!
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 ofheight: 100%
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
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).
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 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><a href="" title="">Home</a></li>
<li><a href="" title="">Page 2</a></li>
<li><a href="" title="">Page 3</a></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()
}
}
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.
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:
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.
Source: Stackoverflow.com