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