I am creating a header that once scrolled to a certain amount of pixels it fixes and stays in place.
Can I do this using just css and html or do i need jquery too?
I have created a demo so you can understand. Any help would be great!
body{
margin:0px;
padding:0px;
}
.clear{
clear:both;
}
.container{
height:2000px;
}
.cover-photo-container{
width:700px;
height: 348px;
margin-bottom: 20px;
background-color:red;
}
.small-box{
width:163px;
height:100px;
border:1px solid blue;
float:left;
}
.sticky-header{
width:700px;
height:50px;
background:orange;
postion:fixed;
}
I have modified the Coop's answer. Please check the example FIDDLE Here's my edits:
$(window).scroll(function(){
if ($(window).scrollTop() >= 330) {
$('.sticky-header').addClass('fixed');
}
else {
$('.sticky-header').removeClass('fixed');
}
});
Just building on Rich's answer, which uses offset.
I modified this as follows:
$sticky
in Rich's example, it wasn't doing anythingI've moved the offset check into a separate function, and called it on document ready as well as on scroll so if the page refreshes with the scroll half-way down the page, it resizes straight-away without having to wait for a scroll trigger
jQuery(document).ready(function($){
var offset = $( "#header" ).offset();
checkOffset();
$(window).scroll(function() {
checkOffset();
});
function checkOffset() {
if ( $(document).scrollTop() > offset.top){
$('#header').addClass('fixed');
} else {
$('#header').removeClass('fixed');
}
}
});
I know Coop has already answered this question, but here is a version which also tracks where in the document the div is, rather than relying on a static value:
Javascript
var offset = $( ".sticky-header" ).offset();
var sticky = document.getElementById("sticky-header")
$(window).scroll(function() {
if ( $('body').scrollTop() > offset.top){
$('.sticky-header').addClass('fixed');
} else {
$('.sticky-header').removeClass('fixed');
}
});
CSS
.fixed{
position: fixed;
top: 0px;
}
If you are using React I recently published a custom hook which enables you to do this. You need to supply a ref to the sticky element as well as a ref to the element you want it to stick to the top of. It handles the screen "jump" (as mentioned in other responses) for you as well.
const sticky = useRef<HTMLDivElement>(null)
const container = useRef<HTMLDivElement>(null)
useStickyScroll({
element: sticky,
container: container
})
The chosen solution did not fit well in my page. So this is a more advanced version that works with bootstrap.
The javascript
var stickyOffset = $('.sticky-header').offset().top;
$(window).scroll(function () {
var sticky = $('.sticky-header'),
scroll = $(window).scrollTop(),
header = $('.fixed-header-background');
sticky.each(function() {
var left = $(this).offset().left;
$(this).data('left', left);//I store the left offset
});
if (scroll >= stickyOffset) {
sticky.addClass('fixed');
header.css('display', 'block');
sticky.each(function() {
$(this).css('left', $(this).data('left'));//I set the left offset
});
} else {
sticky.removeClass('fixed');
header.css('display', 'none');
sticky.each(function () {
$(this).css('left', '');//I remove the left offset
});
}
});
The CSS
.fixed-header-background {
display: none;
position: fixed;
top: 50px;
width: 100%;
height: 30px;
background-color: #fff;
z-index: 5;
border-bottom-style: solid;
border-bottom-color: #dedede;
border-bottom-width: 2px;
}
.fixed{
position: fixed;
top: 52px;
z-index: 6;
}
And the HTML
<div class="fixed-header-background"></div>
<table class="table table-hover table-condensed">
<thead>
<tr>
<th></th>
<th><span class="sticky-header">My header 1</span></th>
<th><span class="sticky-header">My header 2</span></th>
</tr>
</thead>
<tbody>
[....]
</tbody>
</table>
$(document).ready(function(){
var div=$('#header');
var start=$(div).offset().top;
$.event.add(window,'scroll',function(){
var p=$(window).scrollTop();
$(div).css('position',(p>start)?'fixed':'static');
$(div).css('top',(p>start)?'0px':'');
});
});
It works perfectly.
In 2019 with CSS3 you can do this without Javascript at all. I frequently make sticky headers like this:
body {_x000D_
overflow-y: auto;_x000D_
margin: 0;_x000D_
}_x000D_
_x000D_
header {_x000D_
position: sticky; /* Allocates space for the element, but moves it with you when you scroll */_x000D_
top: 0; /* specifies the start position for the sticky behavior - 0 is pretty common */_x000D_
width: 100%;_x000D_
padding: 5px 0 5px 15px;_x000D_
color: white;_x000D_
background-color: #337AB7;_x000D_
margin: 0;_x000D_
}_x000D_
_x000D_
h1 {_x000D_
margin: 0;_x000D_
}_x000D_
_x000D_
div.big {_x000D_
width: 100%;_x000D_
min-height: 150vh;_x000D_
background-color: #1ABB9C;_x000D_
padding: 10px;_x000D_
}
_x000D_
<body>_x000D_
<header><h1>Testquest</h1></header>_x000D_
<div class="big">Just something big enough to scroll on</div>_x000D_
</body>
_x000D_
The simplest way is: HTML:
<header>
<h1>Website</h1>
</header>
CSS:
header{
position: sticky;
top: 0;
}
custom scroll Header Fixed in shopify:
$(window).scroll(function(){
var sticky = $('.site-header'),
scroll = $(window).scrollTop();
if (scroll >= 100) sticky.addClass('fixed');
else sticky.removeClass('fixed');
})
css:
header.site-header.border-bottom.logo--left.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 9;
}
Coops answer is a good, simple solution, however, once you apply the fixed class the page becomes that much shorter and content will "jump" up, and if page is of a length where the scroll distance is less than the height of the header element, it will appear to jump and not let you see the bottom of the page.
The answer I found was to add a spacer div above the element that will become fixed (nav element in my case), and set it to the same height as the nav element, and set it to display none.
When adding the .fixed class to the nav, show the .nav-spacer div, and when removing it, hide it. Since the height of the page changes instantly I have set the duration to 0.
HTML
<header>the element above the element that will become fixed</header>
<div class="nav-spacer"></div>
<nav></nav>
CSS
nav {
position: relative;
height: 100px;
}
.nav-spacer{
position: relative;
height: 100px;
display: none;
}
.fixed {
position: fixed;
top:0;
left:0;
width: 100%;
/* I like to add a shadow on to the fixed element */
-webkit-box-shadow: 0px 5px 10px 1px rgba(0,0,0,0.25);
-moz-box-shadow: 0px 5px 10px 1px rgba(0,0,0,0.25);
box-shadow: 0px 5px 10px 1px rgba(0,0,0,0.25);
}
JavaScript
var stickyOffset = $('nav').offset().top;
$(window).scroll(function(){
if ($(window).scrollTop() >= stickyOffset){
$('nav').addClass('fixed');
//this makes the page length equal to what it was before fixing nav
$('.nav-spacer').show(0);
}
else {
$('nav').removeClass('fixed');
$('.nav-spacer').hide(0);
}
});
Hopefully this one piece of an alternate solution will be as valuable to someone else as it was for me.
Situation:
In an HTML5 page I had a menu that was a nav element inside a header (not THE header but a header in another element).
I wanted the navigation to stick to the top once a user scrolled to it, but previous to this the header was absolute positioned (so I could have it overlay something else slightly).
The solutions above never triggered a change because .offsetTop was not going to change as this was an absolute positioned element. Additionally the .scrollTop property was simply the top of the top most element... that is to say 0 and always would be 0.
Any tests I performed utilizing these two (and same with getBoundingClientRect results) would not tell me if the top of the navigation bar ever scrolled to the top of the viewable page (again, as reported in console, they simply stayed the same numbers while scrolling occurred).
Solution
The solution for me was utilizing
window.visualViewport.pageTop
The value of the pageTop property reflects the viewable section of the screen, therefore allowing me to track where an element is in reference to the boundaries of the viewable area.
This allowed a simple function assigned to the scroll event of the window to detect when the top of the navigation bar intersected with the top of the viewable area and apply the styling to make it stick to the top.
Probably unnecessary to say, anytime I am dealing with scrolling I expect to use this solution to programatically respond to movement of elements being scrolled.
Hope it helps someone else.
Coop's answer is excellent.
However it depends on jQuery, here is a version that has no dependencies:
HTML
<div id="sticky" class="sticky"></div>
CSS
.sticky {
width: 100%
}
.fixed {
position: fixed;
top:0;
}
JS
(This uses eyelidlessness's answer for finding offsets in Vanilla JS.)
function findOffset(element) {
var top = 0, left = 0;
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while(element);
return {
top: top,
left: left
};
}
window.onload = function () {
var stickyHeader = document.getElementById('sticky');
var headerOffset = findOffset(stickyHeader);
window.onscroll = function() {
// body.scrollTop is deprecated and no longer available on Firefox
var bodyScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (bodyScrollTop > headerOffset.top) {
stickyHeader.classList.add('fixed');
} else {
stickyHeader.classList.remove('fixed');
}
};
};
Example
Or just simply add a span
tag with the height of the fixed header set as its height then insert it next to the sticky header:
$(function() {
var $span_height = $('.fixed-header').height;
var $span_tag = '<span style="display:block; height:' + $span_height + 'px"></span>';
$('.fixed-header').after($span_tag);
});
Source: Stackoverflow.com