javascript – Scrolling child div scrolls the window, how do I stop that?-ThrowExceptions

Exception or error:

I have a div, with a scroll bar, When it reaches the end, my page starts scrolling. Is there anyway I can stop this behavior ?

How to solve:

You can inactivate the scrolling of the whole page by doing something like this:

<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>

###

Found the solution.

http://jsbin.com/itajok

This is what I needed.

And this is the code.

http://jsbin.com/itajok/edit#javascript,html

Uses a jQuery Plug-in.


Update due to deprecation notice

From jquery-mousewheel:

The old behavior of adding three arguments (delta, deltaX, and deltaY)
to the event handler is now deprecated and will be removed in later
releases.

Then, event.deltaY must now be used:

var toolbox = $('#toolbox'),
    height = toolbox.height(),
    scrollHeight = toolbox.get(0).scrollHeight;

toolbox.off("mousewheel").on("mousewheel", function (event) {
  var blockScrolling = this.scrollTop === scrollHeight - height && event.deltaY < 0 || this.scrollTop === 0 && event.deltaY > 0;
  return !blockScrolling;
});

Demo

###

The selected solution is a work of art. Thought it was worthy of a plugin….

$.fn.scrollGuard = function() {
    return this
        .on( 'wheel', function ( e ) {
            var event = e.originalEvent;
            var d = event.wheelDelta || -event.detail;
            this.scrollTop += ( d < 0 ? 1 : -1 ) * 30;
            e.preventDefault();
        });
};    

This has been an ongoing inconvenience for me and this solution is so clean compared to other hacks I’ve seen. Curious to know how more about how it works and how widely supported it would be, but cheers to Jeevan and whoever originally came up with this. BTW – stackoverflow answer editor needs this!

UPDATE

I believe this is better in that it doesn’t try to manipulate the DOM at all, only prevents bubbling conditionally…

$.fn.scrollGuard2 = function() {
  return this
    .on( 'wheel', function ( e ) {
      var $this = $(this);
      if (e.originalEvent.deltaY < 0) {
        /* scrolling up */
        return ($this.scrollTop() > 0);
      } else {
        /* scrolling down */
        return ($this.scrollTop() + $this.innerHeight() < $this[0].scrollHeight);
      }
    })
  ;
};    

Works great in chrome and much simpler than other solutions… let me know how it fares elsewhere…

FIDDLE

###

You could use a mouseover event on the div to disable the body scrollbar and then a mouseout event to activate it again?

E.g. The HTML

<div onmouseover="disableBodyScroll();" onmouseout="enableBodyScroll();">
    content
</div>

And then the javascript like so:

var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
    body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
    body.style.overflowY = 'auto';
}

###

If I understand your question correctly, then you want to prevent scrolling of the main content when the mouse is over a div (let’s say a sidebar). For that, the sidebar may not be a child of the scrolling container of the main content (which was the browser window), to prevent the scroll event from bubbling up to its parent.

This possibly requires some markup changes in the following manner:

<div id="wrapper"> 
    <div id="content"> 
    </div> 
</div> 
<div id="sidebar"> 
</div> 

See it’s working in this sample fiddle and compare that with this sample fiddle which has a slightly different mouse leave behavior of the sidebar.

See also scroll only one particular div with browser’s main scrollbar.

###

I wrote resolving for this issue

  var div;
  div = document.getElementsByClassName('selector')[0];

  div.addEventListener('mousewheel', function(e) {
    if (div.clientHeight + div.scrollTop + e.deltaY >= div.scrollHeight) {
      e.preventDefault();
      div.scrollTop = div.scrollHeight;
    } else if (div.scrollTop + e.deltaY <= 0) {
      e.preventDefault();
      div.scrollTop = 0;
    }
  }, false);

###

Here’s a cross-browser way to do this on the Y axis, it works on desktop and mobile. Tested on OSX and iOS.

var scrollArea = this.querySelector(".scroll-area");
scrollArea.addEventListener("wheel", function() {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var deltaY = event.deltaY;
    if ( (scrollTop === maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
});

scrollArea.addEventListener("touchstart", function(event) {
    this.previousClientY = event.touches[0].clientY;
});

scrollArea.addEventListener("touchmove", function(event) {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var currentClientY = event.touches[0].clientY;
    var deltaY = this.previousClientY - currentClientY;
    if ( (scrollTop === maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
    this.previousClientY = currentClientY;
});

###

this disables the scrolling on the window if you enter the selector element.
works like charms.

elements = $(".selector");

elements.on('mouseenter', function() {
    window.currentScrollTop = $(window).scrollTop();
    window.currentScrollLeft = $(window).scrollTop();
    $(window).on("scroll.prevent", function() {
        $(window).scrollTop(window.currentScrollTop);
        $(window).scrollLeft(window.currentScrollLeft);
    });
});

elements.on('mouseleave', function() {
    $(window).off("scroll.prevent");
});

###

You can inactivate the scrolling of the whole page by doing something like this but display the scrollbar!

<div onmouseover="document.body.style.overflow='hidden'; document.body.style.position='fixed';" onmouseout="document.body.style.overflow='auto'; document.body.style.position='relative';"></div>

###

$this.find('.scrollingDiv').on('mousewheel DOMMouseScroll', function (e) {
  var delta = -e.originalEvent.wheelDelta || e.originalEvent.detail;
  var scrollTop = this.scrollTop;
  if((delta < 0 && scrollTop === 0) || (delta > 0 && this.scrollHeight - this.clientHeight - scrollTop === 0)) {
    e.preventDefault();
  }
});

###

As answered here, most modern browsers now support the overscroll-behavior: none; CSS property, that prevents scroll chaining. And that’s it, just one line!

###

Based on ceed’s answer, here is a version that allows nesting scroll guarded elements. Only the element the mouse is over will scroll, and it scrolls quite smoothly. This version is also re-entrant. It can be used multiple times on the same element and will correctly remove and reinstall the handlers.

jQuery.fn.scrollGuard = function() {
    this
        .addClass('scroll-guarding')
        .off('.scrollGuard').on('mouseenter.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g[0].myCst = $g.scrollTop();
            $g[0].myCsl = $g.scrollLeft();
            $g.off("scroll.prevent").on("scroll.prevent", function() {
                $g.scrollTop($g[0].myCst);
                $g.scrollLeft($g[0].myCsl);
            });
        })
        .on('mouseleave.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g.off("scroll.prevent");
        });
};

One easy way to use is to add a class, such as scroll-guard, to all the elements in the page that you allow scrolling on. Then use $('.scroll-guard').scrollGuard() to guard them.

###

If you apply an overflow: hidden style it should go away

edit: actually I read your question wrong, that will only hide the scroll bar but I don’t think that’s what you are looking for.

###

I couldn’t get any of the answers to work in Chrome and Firefox, so I came up with this amalgamation:

$someElement.on('mousewheel DOMMouseScroll', scrollProtection);

function scrollProtection(event) {
    var $this = $(this);
    event = event.originalEvent;
    var direction = (event.wheelDelta * -1) || (event.detail);
    if (direction < 0) {
        if ($this.scrollTop() <= 0) {
            return false;
        }
    } else {
        if ($this.scrollTop() + $this.innerHeight() >= $this[0].scrollHeight) {
            return false;
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *