JSFiddle - React, Tailwind, and code Playground

by wrxsti85

HTML

<script src="//code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"></script>
<script src="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<nav></nav>

<div class="container">
  <div class="row">
    <div class="col col-3">
        <div id="sidebar">Sidebar</div>
    </div>
    <div class="col col-9">
        <div class="content">Content</div>
    </div>
  </div>
</div>
<footer></footer>

CSS

.row {
  margin-top: 20px;
}

#sidebar, .content {
  border: solid 1px #6c757d;
  padding: 10px;
  background: #f8f9fa;
}

#sidebar {
    height: 1000px;
    width: 255px;
}

.content {
    height: 2000px;
    margin-bottom: 50px;
}

nav, footer {
    height: 600px;
    background: #f8f9fa;
    border: solid 1px #6c757d;
}

nav {
    margin-bottom: 50px;
}

.position-fixed {
    position: fixed;
}

.position-relative {
    position: relative;
}

.anim {
    -webkit-transition: all .4s;
    -moz-transition: all .4s;
    -ms-transition: all .4s;
    -o-transition: all .4s;
    transition: all .4s;
}

JavaScript

var lastScroll = 0,
	lastScrollDirection = true; // scrollingdown

$(window).on('scroll', function(){
	var topAnchorPad 		= 50,
    	topAnchor 			= $('nav').offset().top + $('nav').outerHeight(true),
        bottomAnchorPad 	= 50,
        bottomAnchor 		= $('footer').offset().top,
        sidebar 			= $('#sidebar'),
        sidebarHeight 		= sidebar.outerHeight(false),
        sidebarTop			= sidebar.offset().top,
        sidebarBottom		= sidebarTop + sidebarHeight,
        scrollTop 			= $(window).scrollTop(),
        windowHeight 		= window.innerHeight,
        docHeight 			= $(document).height(),
        isAtTop				= (scrollTop <= topAnchor - (topAnchorPad / 2)),
        isAtBottom			= (scrollTop + sidebarHeight >= bottomAnchor - bottomAnchorPad),
        scrollingDown		= lastScroll < scrollTop,
        isShortSidebar		= sidebarHeight < windowHeight,
        directionChanged	= scrollingDown != lastScrollDirection,
        hasCollapsingNav	= false;
        
    console.log(scrollTop, sidebarTop)

    if (isAtTop)
    {
        sidebar
            .removeClass('position-fixed')
            .addClass('position-relative')
                .css({
                'top': '0px'
            });
    }
    else if (isAtBottom)
    {
        sidebar
            .removeClass('position-fixed')
            .addClass('position-relative')
            .removeAttr('style')
            .css({
                'top': (bottomAnchor - topAnchor - sidebarHeight - topAnchorPad)
            });
    }
    else if (isShortSidebar)
    {
        sidebar
            .removeClass('position-relative')
            .addClass('position-fixed')
            .removeAttr('style')
            .css({
                'top': topAnchorPad / 2
            });
    }
    else
    {
    	if (scrollingDown && scrollTop >= sidebarHeight - (bottomAnchorPad / 2) ) // snap to bottom
        {
        	console.log('bottom sticky!')
            sidebar
                .removeClass('position-relative')
               ...