JSFiddle - React, Tailwind, and code Playground
by oilvier
HTML
<div class="foo baz">
<header></header>
<nav></nav>
<main>
<div class="bar">
<button type="button">Foo</button>
</div>
</main>
<footer></footer>
</div>
SCSS
*,
*:before,
*:after {box-sizing:border-box;}
.foo.baz {
transform: translate3d(0,0,0);
overflow: hidden;
.has-sticky & {
transform: none !important;
}
}
header {
height:100px;
background-color: rebeccapurple;
}
nav {
height:80px;
background-color: wheat;
}
main {
position: relative;
height: 800px;
background-color: tomato;
}
footer {
height:300px;
background-color: crimson;
}
.bar {
position: fixed;
bottom: 20px;
left: 0;
right: 0;
text-align: center;
opacity: 0;
transition: opacity 0.3s ease-out;
button {
transform: translateY(100%);
transition: transform 0.3s ease-out;
}
&.is-fixed {
position: absolute;
}
.has-sticky & {
opacity: 1;
button {transform: translateY(0);}
}
}
JavaScript
function isElementInViewport (el)
{
//special bonus for those using jQuery
if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];
var rect = el.getBoundingClientRect();
// DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
var windowWidth = (window.innerWidth || document.documentElement.clientWidth);
// http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);
return (vertInView && horInView);
}
/**
* À cause d'un bug sur les éléments en `position:fixed` enfants d'un conteneur
* avec `transform:translate3d(0,0,0)` on annule cette propriété une fois que
* le menu n'est plus visible (`transform:translate3d(0,0,0)` étant utilisé pour
* faire apparaitre le menu en mobile)
*/
var scrolling = false;
// On utilise requestAnimationFrame pour améliorer les performances
function requestStickyKit() {
if(!scrolling) {
requestAnimationFrame(allowStickyKit);
scrolling = true;
}
}
function allowStickyKit() {
if( !isElementInViewport($("header")) ) {
$('html').addClass('has-sticky');
} else {
$('html').removeClass('has-sticky');
}
if( isElementInViewport( $('footer')) ) {
$('.bar').addClass('is-fixed');
} else {
$('.bar').removeClass('is-fixed');
}
// On remet à 0 pour appeler les prochains requestAnimationFrame
scrolling = false;
}
$(window).on("DOMContentLoaded load resize scroll", requestStickyKit);