Here's a start. Basically, we copy the header on load, and then check (using .scrollTop()
or window.scrollY
) to see when the user scrolls beyond a point (e.g. 200pixels). Then we simply toggle a class (in this case .down
) which moves the original into view.
Lastly all we need to do is apply a transition: top 0.2s ease-in
to our clone, so that when it's in the .down
state it slides into view. Dunked does it better, but with a little playing around it's easy to configure
CSS
header {
position: relative;
width: 100%;
height: 60px;
}
header.clone {
position: fixed;
top: -65px;
left: 0;
right: 0;
z-index: 999;
transition: 0.2s top cubic-bezier(.3,.73,.3,.74);
}
body.down header.clone {
top: 0;
}
either Vanilla JS (polyfill as required)
var sticky = {
sticky_after: 200,
init: function() {
this.header = document.getElementsByTagName("header")[0];
this.clone = this.header.cloneNode(true);
this.clone.classList.add("clone");
this.header.insertBefore(this.clone);
this.scroll();
this.events();
},
scroll: function() {
if(window.scrollY > this.sticky_after) {
document.body.classList.add("down");
}
else {
document.body.classList.remove("down");
}
},
events: function() {
window.addEventListener("scroll", this.scroll.bind(this));
}
};
document.addEventListener("DOMContentLoaded", sticky.init.bind(sticky));
or jQuery
$(document).ready(function() {
var $header = $("header"),
$clone = $header.before($header.clone().addClass("clone"));
$(window).on("scroll", function() {
var fromTop = $("body").scrollTop();
$('body').toggleClass("down", (fromTop > 200));
});
});
Newer Reflections
Whilst the above answers the OP's original question of "How does Dunked achieve this effect?", I wouldn't recommend this approach. For starters, copying the entire top navigation could be pretty costly, and there's no real reason why we can't use the original (with a little bit of work).
Furthermore, Paul Irish and others, have written about how animating with translate()
is better than animating with top
. Not only is it more performant, but it also means that you don't need to know the exact height of your element. The above solution would be modified with the following (See JSFiddle):
header.clone {
position: fixed;
top: 0;
left: 0;
right: 0;
transform: translateY(-100%);
transition: 0.2s transform cubic-bezier(.3,.73,.3,.74);
}
body.down header.clone {
transform: translateY(0);
}
The only drawback with using transforms is, that whilst browser support is pretty good, you'll probably want to add vendor prefixed versions to maximize compatibility.