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);