JSFiddle - React, Tailwind, and code Playground

by Luke Marlow

HTML

<h1>Prevent parent scrolling</h1>

<section id="parent-left">      

  <div id="mousewheel-prevented">
    Scrolling trap enabled! <span id="prevented-count"></span>
  </div>
  
  <div class="trapScroll child">              
    
    <div class="scrollable-content">
      <h2>Scrolling (mousewheel / trackpad) in here doesn't scroll parent elements</h2>            
    </div>

    <div class="scroll-down-msg">
      Scroll down <i class="fa-angle-down"></i>
    </div>
    
  </div>
  
  <div id="mousewheel-prevented">
    Scrolling trap enabled! (try scrolling down) <span id="prevented-count"></span>
  </div>
  <p class="notes">Works with: <b>Chrome</b>, <b>IE10+</b>, <b>Firefox</b> and <b>Opera</b>. Enjoy!</p>    
  
  <div class="force-parent-scrollbar"></div>
</section>

<section id="parent-right">
  <div class="child">
    
    <div class="scrollable-content">
      <h2>Scrolling here will scroll its parents</h2>
    </div>
    
    <div class="scroll-down-msg">
      Scroll down <i class="fa-angle-down"></i>
    </div>
  </div>
  
  <div class="force-parent-scrollbar"></div>
</section>

CSS

.trap-mousewheel {
  overflow: auto;    
}

html, body {
  height: 100%;
}

body {
  max-width: 80em;
  margin: 0 auto;
  padding: 0;
  background: #333;
  line-height: 1.3;
}

h1 {
  color: #EEE;
  padding: 0 2rem;
  text-align: center;
}

h2 {
  font-size: 1.2em;  
}

#parent-left,
#parent-right {
  position: relative;
  height: 100%;
  width: 50%;
  padding: 9rem 2rem;
  overflow: auto;
  background: #777;
}

#parent-left {
  float: left;
  border-right: 1px solid #999;
  .child {
    @c: #8205a1;
    color: lighten(@c, 55%);
    background: @c;
  }
}

#parent-right .child {
  @c: #006ba1;
  color: lighten(@c, 35%);
  background: @c;
}

.force-parent-scrollbar {
  height: 85%;
}

.child {
  position: relative;
  //max-width: 25em;
  height: 200px;
  padding: 0 2em;    
  overflow: auto;
}
.scrollable-content {
  height: 120%;
  padding-top: .5em;
}
#mousewheel-prevented {
  //position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 100%;
  margin-top: -2.6rem;
  opacity: 0;
  //display: none;  
  padding: .8em 0;
  color: #FFF;
  font-size: .9em;
  text-align: center;
  background: rgba(0,0,0,.9);
  //background: #8205a1;
}

[class*="fa-"] {
  font-style: normal;
  font-family: FontAwesome;
}

.scroll-down-msg {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 6em;
  margin-left: -3em;
  padding: 1em;
  background: rgba(0,0,0,.3);
  
  i {
    float: right;
  }
}

.notes {
  @c: #66debc;
  color: @c;
  padding: 1em;
  background: rgba(0,0,0,.4);
}

JavaScript

var trapScroll;

(function($){  
  
  trapScroll = function(opt){
    
    var trapElement;
    var scrollableDist;
    var trapClassName = 'trapScroll-enabled';
    var trapSelector = '.trapScroll';
    
    var trapWheel = function(e){
      
      if (!$('body').hasClass(trapClassName)) {
        
        return;
        
      } else {  
        
        var curScrollPos = trapElement.scrollTop();
        var wheelEvent = e.originalEvent;
        var dY = wheelEvent.deltaY;

        // only trap events once we've scrolled to the end
        // or beginning
        if ((dY>0 && curScrollPos >= scrollableDist) ||
            (dY<0 && curScrollPos <= 0)) {

          opt.onScrollEnd();
          return false;
          
        }
        
      }
      
    }
    
    $(document)
      .on('wheel', trapWheel)
      .on('mouseleave', trapSelector, function(){
        
        $('body').removeClass(trapClassName);
      
      })
      .on('mouseenter', trapSelector, function(){   
      
        trapElement = $(this);
        var containerHeight = trapElement.outerHeight();
        var contentHeight = trapElement[0].scrollHeight; // height of scrollable content
        scrollableDist = contentHeight - containerHeight;
        
        if (contentHeight>containerHeight)
          $('body').addClass(trapClassName); 
      
      });       
  } 
  
})($);

var preventedCount = 0;
var showEventPreventedMsg = function(){  
  $('#mousewheel-prevented').stop().animate({opacity: 1}, 'fast');
}
var hideEventPreventedMsg = function(){
  $('#mousewheel-prevented').stop().animate({opacity: 0}, 'fast');
}
var addPreventedCount = function(){
  $('#prevented-count').html('prevented <small>x</small>' + preventedCount++);
}

trapScroll({ onScrollEnd: addPreventedCount });
$('.trapScroll')
  .on('mouseenter', showEventPreventedMsg)
  .on('mouseleave', hideEventPreventedMsg);      
$('[id*="parent"]').scrollTop(100);