JSFiddle - React, Tailwind, and code Playground

by Marlin Forbes

HTML

<div id="header"></div>
<div id="content">
    <div class="side" id="stick">STICKY</div>
    <div class="main"></div>
    <div class="main"></div>
    <div class="main"></div>
    <div class="main"></div>
    <div class="main"></div>
</div>
<div id="footer"></div>
<script>

CSS

body {
    margin: 0;
    padding: 10px;
}
#header {
    height: 100px;
    margin: 0 0 10px;
    background: red;
}
#content {
    position: relative;
    float: left;
    width: 100%;
    height: auto;
    margin: 0 -110px 0 0;
}
.side {
    float: right;
    width: 100px;
    height: 900px;
    margin: 0 0 0 10px;
    background: linear-gradient(red, yellow);
}
.main {
    height: 600px;
    margin: 0 110px 10px 0;
    background: lightgray;
}
#footer {
    clear: both;
    height: 100px;
    background: orange;
}

CoffeeScript

$.fn.stickyTopBottom = (options = {}) ->
    # only works in browsers that support CSS3 transforms (i.e. IE9+)

    ## ##############
    #initialization
    options = $.extend  
      container: $('body') #reference element for starting and stopping the sticking (doesn't actually have to contain the element)
      top_offset: 0 #distance from top of viewport to stick top of element
      bottom_offset: 0 #distance from bottom of viewport to stick bottom of element
    , options

    $el = $(this)

    #Get the top of the reference element. If the container moves, would need to move this into scroll handler. 
    #If the container is translated Y, then this method will fail I believe.
    container_top = options.container.offset().top 
    element_top = $el.offset().top

    viewport_height = $(window).height()
    $(window).on 'resize', -> 
      viewport_height = $(window).height()


    ## #################
    # The meat: scroll handler
    #
    # When moving up or element is shorter than viewport:
    #    if scrolled above top of element, position top of element to top of viewport
    #      (stick to top)
    # When moving down: 
    #    if scrolled past bottom of element, position bottom of element at bottom of viewport
    #      (stick to bottom)

    current_translate = 0
    last_viewport_top = document.documentElement.scrollTop || document.body.scrollTop
    $(window).scroll (event) ->
      viewport_top = document.documentElement.scrollTop || document.body.scrollTop
      viewport_bottom = viewport_top + viewport_height
      effective_viewport_top = viewport_top + options.top_offset
      effective_viewport_bottom = viewport_bottom - options.bottom_offset

      # Need to reset element's height each scroll event because it may have change height 
      # since initialization.
      # Warning: checking height is performance no-no
      element_height = $el.height()

      is_scrolling_up = viewport_top < last_viewport_top
     ...