I have this HTML code:
<div class="header">
<div class="desc">Description</div>
<div class="logo"><img src=""/></div>
<div class="navbar"></div></div>
.header has a height of 150px. .navbar has a height of 20px. When the user scrolls, I want .navbar to stick at the top. So I went to the CSS and set position:sticky and top:0. But this didn't work. I initially thought that firefox is not supporting position:sticky, but that's not the case because I was able to see a working demo of it. I googled about it but found nothing helpful. Anyone knows why this is not working?
Position sticky was not working for me due to the body element having overflow-x: hidden; set.
The 2 most common culprits why position: sticky; might not work are:
You haven't defined top: 0;, bottom: 0;, left: 0 or something similar
One of the parents of your sticky element has overflow (x or y) set to hidden, scroll or auto.
For me it was the first one.
It works fine if you move the navbar outside the header. See below. For the reason, according to MDN:
The element is positioned according to the normal flow of the document, and then offset relative to its flow root and containing block based on the values of top, right, bottom, and left.
For the containing block:
The containing block is the ancestor to which the element is relatively positioned
So, when I do not misunderstand, the navbar is positioned at offset 0 within the header as soon as it is scrolled outside the viewport (which, clearly, means, you can't see it anymore).
.navbar {
background: hotpink;
width: 100%;
height: 50px;
position: sticky;
top: 0;
}
.header {
height: 150px;
background: grey;
}
body {
height: 800px;
position: relative;
}
<div class="header">
<div class="desc">Description</div>
<div class="logo"><img src="" /></div>
</div>
<div class="navbar"></div>
To expand from the answers above and some information to make it work with flexbox parent and overflow other than visible (the examples below assume you use vertical - sticky with either top or bottom set to a certain value and position set to sticky):
The most frequent case is you have an ancestor element (not just immediate parent) with overflow property set to something other than visible and as a result there is no space is left to stick around.
To quickly find out if this is the case, you can run this script in the browser console (please make sure you change the .your-sticky-element class to your element's selector):
var stickyElement = document.querySelector('.your-sticky-element');
var parent = stickyElement.parentElement;
while (parent) {
var hasOverflow = getComputedStyle(parent).overflow;
if(hasOverflow != 'visible') {
console.log(hasOverflow, parent);
}
parent = parent.parentElement;
}
SOLUTION:
a) If you found there is overflow set, and you can remove it, this should solve it
b) If you have to keep your overflow setting, you have to make the parent element's height higher than the sticky element's height. If the parent element has no height or the sticky element fills up all the height, it means there is simply no place to stick within when the page is scrolled. It doesn't need to an explicit height (vertical), but you can inspect to see if your sticky element has extra space left after itself.
Parent is not higher than the sticky element to leave extra space. This particular case can be caused by different circumstances but the solution to this is the same above, please see 1.b
If your sticky element's parent is a flexbox (align-items has default value of normal) or grid, and if the sticky element itself doesn't have a proper align-self set, there will be no space left for the sticky element to hold when scrolling (for example, if it is align-self: stretch or auto [default value]). This is because the child element is stretched to fill up the height of the parent.
SOLUTION:
In this case, align-self: flex-start set for the sticky element can fix the problem because in the element will stand at the start, leaving extra space after itself.
Guide: There are much more complex circumstances both in the case of flexboxes and without it, but the general rule of thumb is your sticky element needs space within the parent to be sticky when scrolled.
Somehow your code only works when the .navbar element is not inside another container like the header. I moved it out and then it works fine. I created a codepen snippet for that, check it out
<header>
<div class="logo">Logo</div>
<div class="description"><div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo, veritatis.</div></div>
</header>
<div class="navbar">
<ul>
<li>navitem1</li>
<li>navitem2</li>
<li>navitem3</li>
<li>navitem4</li>
</ul>
</div>
Right now position:sticky is supported quite good as you can see on canIuse. Of course IE currently has no support but the new Edge version will bring full support for this! I found some interesting articles about this topic:
Working demo (chrome,firefox 👍) https://codepen.io/simevidas/pen/JbdJRZ
Caniuse refernce: http://caniuse.com/#search=sticky
sticky article on MDN including latest browser support table https://developer.mozilla.org/en-US/docs/Web/CSS/position#Sticky_positioning
But there are good news on the horizon. I think better browser support will follow the next time.
Adding more content after nav inside header provides sticky behavior, but only for a moment - if the user scrolls down too much, nav will disappear with header, since it can't jump out below header's bottom border.
Thus, the only solution with pure CSS is to put nav inside element that is partially visible even after the user scrolls to the bottom of the page (directly inside body or inside some sort of container that spans to the bottom of the page or at least to the footer).
If this solution is not possible, the other way is to use JavaScript. Before transitioning to CSS, I used the following code (found similar jQuery solution somewhere long time ago, don't remember where, so the credit goes to the anonymous author; Vanilla JS can be easily obtained from this):
$(document).ready(function () {
var sticky_navigation_offset_top = $('nav').offset().top;
var sticky_navigation = function () {
var scroll_top = $(window).scrollTop();
if (scroll_top > sticky_navigation_offset_top) {
$('nav').css({
'position': 'fixed',
'top': 0,
'left': 0,
'right': 0,
'margin-left': 'auto',
'margin-right': 'auto'
});
} else {
$('nav').css({
'position': 'relative'
});
}
};
sticky_navigation();
$(window).scroll(function () {
sticky_navigation();
});
});
Looks like if you try to set sticky a container which has many children nodes inside, instead of them being wrapped in a div, and the parent of sticky container is flex, then it will not sticky. Just wrap the childs in a div fixed it for me.
Your HTML code as it is and write CSS class for navigation bar:
.header {
height: 150px;
background-color: #d1d1d1;
}
.navbar {
background: #999;
border-bottom: 1px solid #333;
border-top: 1px solid #333;
color: #FFF;
margin: 0;
padding: 2px 0 0 12px;
position: sticky;
top: -1px;
}
<div class="header">
<div class="desc">Description</div>
<div class="logo"><img src="" /></div>
<div class="navbar"></div>
</div>
Hope this will help
Met some not evident behaviour of horizontal sticky: if width is 100%, then sticky does not work. Width should be less, then container size.
My sticky header would only partly work ... after a couple of scrolls it would disappear but would work initially
It appears the problem was that I had the parent set to height 100%.
I didn't actually need this as the body one was enough so I removed and it and all was good.. sticks forever
Although this now breaks my footer from staying on the bottom when their is no content!
No huge compromises of the HTML structure need to be made to fix this issue. Simply add display: inline; to all of the sticky element's parents up until you get to the element you wish the sticky element to stick to.
Just to add something to #user56reinstatemonica8 great point...
If immediate parent of sticky node has display: flex sticky positioning could not work.
My guess is that culprit is align-items: stretch as default.
In a flex-direction: row scenario, align-items: stretch let children's height grow so that they are equal height.
So, to overcome this and make sticky work as expected with display: flex you can:
define align-items as center | start | baseline to immediate parent that has display: flex.
define align-self as center | start | baseline to sticky node.
define an explicit height to sticky node.
This question already has answers here:
Why doesn't the height of a container element increase if it contains floated elements?
(7 answers)
Closed 8 years ago.
I make simple http://jsfiddle.net/6KzXw/ with CSS:
.container {
width: 50%;
margin: 0 auto;
padding: 2px;
background: red;
}
.left {
float: left;
background: yellow;
}
.right {
float: right;
background: yellow;
}
and HTML:
<div class="container">
<div class="left">To the left.</div>
<div class="right">To the right.</div>
</div>
I wondering why area of container isn't red....
After search I found solution with overflow: hidden but official docs about fix: http://www.w3.org/TR/CSS21/visufx.html make me cry when I try to understand how it work...
Can any explain why overflow: hidden is fix for surrounding problem with standard in mind?
overflow: hidden causes the container to establish a block formatting context for its contents. Without it, the floats participate in some other formatting context, having been taken out of normal flow, and therefore the floats are not taken into account when calculating the height of the container.
Once you cause the container to establish a formatting context, it will consider the floats, even though the floats are still taken out of the normal flow that is established within its own formatting context. That is stated in another section of the spec. The reason this isn't stated in the section that you link to is because it's a side effect that was never really intended, but made so due to implementation limits. See this answer (and the one that it links to) for an explanation.
you need to provide a height to the div as if you float the contents, the contents are removed from the flow of the page. essentially the div sees no children inside it as the children are floating.
i added a height to the div height: 20px
FIDDLE
When you apply the 'hidden' property to an element, any floats within it will take up space. So if you have a container that only contains floated elements, that container will act like it's empty. By setting 'overflow' to 'hidden' we force that container to account for those floats.
Another solution to this is to add a "clearfix" element below the floats. It might look something like this:
<div class="container">
<div class="left">To the left.</div>
<div class="right">To the right.</div>
<div class="clearfix"></div>
</div>
And the CSS will be something like this:
.clearfix {
clear: both;
}
Personally, I prefer setting overflow to hidden (if possible) but there are many clearfix solutions out there.
http://nicolasgallagher.com/micro-clearfix-hack/
http://css-tricks.com/snippets/css/clear-fix/
http://learnlayout.com/clearfix.html
Edit:
As far as setting a set height. You can do that if you want a set height, but if you want the container to grow or shrink based on the height on the floats, you need to set overflow hidden or use a clearfix.
Because the container has a height of 0
Let's say I have a block element, such as an h2:
<h2>Title</h2>
And I give it a background color. The background will span the entire width of the wrapper (as it should).
If I float it, or position: absolute, it will 'shrink wrap' the words. However, both of these methods take the element out of the 'flow' and prevent it from pushing the rest of the content down the page.
I'd like to avoid having to add a clear underneath the title every time. Is there a better solution? I thought the overflow property could do it, but I'm not figuring it out.
fiddle:
http://jsfiddle.net/QxRRh/
Here's one (very simple) way ...
h2 {
display: table;
}
Fiddle
Wrap the header text in a <span> and apply the background color to that span.
HTML:
<h2><span class="blue">Title</span></h2>
CSS:
.blue{
background-color:blue;
}
To explain my problem, I'm trying to make a div wide enough to accommodate a dynamically generated title without wrapping it, but the div also has other content, which I want to wrap.
In other words:
CSS:
.box {
min-width:170px;
}
.box span.title {
font-size:24px;
white-space: nowrap;
}
.box span.text{
font-size:10px;
white-space: normal;
}
HTML:
<div class="box">
<span class="title">Title on one line</span><br />
<span class="text">This is the main body of text which I want to wrap as
required and have no effect on the width of the div.</span>
</div>
However, this is causing the div to expand to be wide enough to contain the main body of text on one line, which I want to wrap. I've tried various arrangements for CSS and the putting them all inside container divs and the like but I can't seem to get the box to be exactly wide enough to contain only the title without wrapping (but not less than the min width)
Is there any way to do this just in CSS? Note I don't want to set a max width as this just causes it to become a static size again, as the main body of text is always going to be enough to hit the max width. I also can't line break the body manually as it's dynamically generated.
Is this (jsFiddle) what you're trying to accomplish?
I just added display: table; to .box's CSS. This expands the main div to the width of the title span but wraps the text span.
Note: You can also set a constant width to prevent the div from expanding to the width of the window. This way it will still expand to the width of the title if it is larger than your constant width, but will not grow if the user drags out the window. In my example I added width: 100px; to demonstrate.
A working jQuery example:
http://jsfiddle.net/8AFcv/
$(function() {
$(".box").width($(".title").width());
})
For headlines you should use the <hN> tags (<h1>, <h2> etc).
For no text wrap:
white-space: nowrap;
On the element who's text you don't want to wrap.
Working Example on jsFiddle
If i understand your correctly you can easily set the same width for yours text as for yours title using JS or jQuery, for ex:
$('.text').width($('.title').width())
and run it at jQuery(document).ready or by event if you add it dynamically
Block elements such as divs extend as far as content pushes them, unless specified by explicit widths or heights.
A pure CSS solution for this is unlikely without setting a max-width on the div.
A pointer on CSS:
Don't include the tags in your selectors (i.e. tag.class) as you are then forced to use that tag with that class. Simply using .class will make it easier to change your markup (should you need to) as well as make your class extend its use to more than a single tag.
I have a <a> in a <div> but the <div> doesn't recongize the <a> and adjust and increase its height to accomodate the link.
Check it out my html/css here: http://jsfiddle.net/RjfVN/2/
That is because your section-link class has float: left;
You need to clear the float. To do this you can add a div directly after the closing </a> like:
<div style="clear: left;"></div>
Or
<div class="clearfix"></div>
And add .clearfix{ clear: both; } to your css so you can reuse it.
Put overflow:auto to your #main.
Yep, your link is using float:left. There are a few ways to fix it.
Have something after the link (like #footer for instance) clear: left (or both).
Float the #main container div as well, or give it position: absolute
Add overflow: auto to the #main container div.
However, in cases 2 & 3 you will need to set the width of the container as well, since it won't have the initial 100% width that it's using. Also, in Internet explorer, setting the width is actually required for the overflow to have any effect because it forces the container to hasLayout.