96

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 ?

Jeevan
  • 3,878
  • 6
  • 29
  • 37
  • 2
    @NGLN not a duplicate, different question. – sidewinderguy Apr 12 '16 at 02:46
  • 1
    Possible duplicate of [Prevent scrolling of parent element when inner element scroll position reaches top/bottom?](https://stackoverflow.com/questions/5802467/prevent-scrolling-of-parent-element-when-inner-element-scroll-position-reaches-t) – TylerH Jul 03 '18 at 15:27

14 Answers14

67

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>
Teemu
  • 22,918
  • 7
  • 53
  • 106
  • 33
    Good, but it makes the browser's scrollbar disappear whenever you hover over the div. – cstack Aug 09 '12 at 19:26
  • @cstack you are right that's why OP (Jeevan) own answer is better. – Imran Bughio Jul 04 '14 at 07:03
  • 2
    Many browser scroll wheels disappear and reappear based on the user moving the mouse now, so the above comment is problem no longer an issue. – Keith Holliday Oct 16 '15 at 03:42
  • This solution does not work when the page is embedded in an iframe though. – Gautam Krishnan Nov 26 '15 at 10:16
  • 5
    not a solution. setting the overflow to hidden hides the scrollbar which suddenly reformats all the contents of the outer element. also, this assumes the outer element is body but it could be another scrolling div. – robisrob Dec 17 '15 at 16:25
  • If you have a fullscreen overlay kinda thing, adding 'max-height: 100vh;' might also help. Just adding the overflow-y didn't work for me! You might also need to store the previous body scroll position, and restore it later, in that case. – Ryan Weiss Aug 06 '16 at 20:31
  • @robisrob: you could add a `margin-right` as big as the scrollbar to fix that kind of problem. Fast fix if you got jQuery: `onmouseover="document.body.style.setProperty('margin-right', window.innerWidth-$(document).width() + 'px');document.body.style.overflow='hidden'"` – Jacob van Lingen Dec 13 '17 at 10:12
  • still a kluge. you will fight this every time you change your css. the most logical way to do this is also the simplest - intercept and stop propagation of the scroll event. Not weird formatting or tricks, just detect if scrolling is appropriate and return false if not. see `scrollGuard2()` method below. – robisrob Dec 14 '17 at 21:20
43

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

sp00m
  • 47,968
  • 31
  • 142
  • 252
Jeevan
  • 3,878
  • 6
  • 29
  • 37
16

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

Community
  • 1
  • 1
robisrob
  • 940
  • 12
  • 23
  • 3
    adding DOMMouseScroll event makes it work with Firefox → http://jsfiddle.net/chodorowicz/egqy7mbz/1/ – chodorowicz Jul 03 '15 at 17:35
  • I am surprised this is not written into jquery. I thought one of the main points was not having to care what browser you're using. – robisrob Dec 14 '15 at 17:09
  • actually it looks like both are deprecated... [`wheel` event](https://developer.mozilla.org/en-US/docs/Web/Events/wheel) – robisrob Dec 14 '15 at 17:12
  • This results in noticeably jerkier scrolling in Chrome on my Macbook. The accepted solution does not have this problem. – danvk Dec 16 '15 at 18:33
  • disappointing. there are so many different versions of this. seems like an important aspect of ui, and really it's surprising that this is the default behavior when it's so unintuitive. Obviously the browser is equipped to know the inner scrolling div is at its limit to be able to apply the event to the window instead, so why aren't there handles available to query it? – robisrob Dec 16 '15 at 23:37
  • I updated this answer to standardized `wheel` event and `deltaY` property, simplified overall and more intuitive. Let me know what you think – robisrob Dec 18 '15 at 17:06
  • Anyone having issues with the scrollbar not scrolling all the way to the top of the scrollable div? Or is that intended? Edit: Maybe it's b/c I'm configuring this within a flexbox table... – Con Antonakos Mar 04 '16 at 22:17
  • 'scrollGuard' (not 2) seems slow to roll, because? – Fábio Zangirolami Jan 27 '18 at 02:07
8

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';
}
joe92
  • 635
  • 2
  • 11
  • 19
8

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();
    }
}, {passive:false});

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

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;
}, {passive:false});
Patrick Matte
  • 171
  • 2
  • 6
  • This works awesome ! Wow, exactly what i searched. It needs definitely more upvotes. – delato468 Sep 01 '17 at 14:16
  • This solution works pretty well but need a little adjust : need to change the `scrollTop === maxScroll` by `scrollTop >= maxScroll`. It seems to be weird by on 1 case, it was needed – tchiot.ludo Dec 21 '17 at 11:32
8

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!

Andriy Stolyar
  • 585
  • 9
  • 21
  • 2
    This should be the correct answer in 2020. For my use case the best solution was `overscroll-behavior: contain;`. – Pier Jan 14 '20 at 20:48
  • this is not fully supported for some edge versions. any idea how to solve this in a different way? – YTG Sep 22 '20 at 18:53
7

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);
Alex Golovin
  • 232
  • 3
  • 6
  • One of the few solutions here that works without the page elements annoyingly jumping due to overflow hidden being set. – Sceptic Nov 11 '15 at 14:55
  • +1 this is a solid solution. if you attach this to a scrollable container you'll want div in this example to be `e.currentTarget`. – Matt Dec 04 '16 at 11:26
  • very clean solution. Thanks – Thinh Bui Jun 14 '17 at 14:57
  • Did work in Chrome 61.0.3163.100 and Safari 11.0, but not in FireFox 56.0 (Mac OS El Capitan) – BigJ Oct 06 '17 at 17:32
  • This is the best native solution. However, `false` does not need to be specified for `addEventListener` since that's the default. Moreover, the comparisons should be simple inequalities (`>` and `<`), not `>=` or `<=`. – Quolonel Questions Dec 31 '17 at 01:26
6

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.

Community
  • 1
  • 1
NGLN
  • 43,011
  • 8
  • 105
  • 200
  • In this case the sidebar is outside the wrapper div. So I don't think the scroll will bubble to the parent – Jeevan Apr 19 '12 at 05:03
  • By "this case" you mean your case? If not: you are exactly right. If so: if your page scrolls at end of div scrolling, then the scroll message bubbles to the div parent, so I think your page and div are not siblings, but parent and child. – NGLN Apr 19 '12 at 06:22
  • Yes, Page is the parent of my div, So on reaching the end, it starts scrolling the page, which I don't want to. – Jeevan Apr 19 '12 at 08:06
  • If you cannot make your div a sibling of the main content, then this issue cannot be fixed with HTML+CSS alone. It is default behaviour. – NGLN Apr 19 '12 at 09:31
  • 1
    I think this is the most efficient solution of all provided answers; with good clean markup at the base of a page, far less CSS and JS hacks are required in order to get the desired behaviour. –  Oct 15 '14 at 16:05
3

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");
});
ceed
  • 689
  • 1
  • 6
  • 15
  • This is indeed a working solution. I needed it in pure js, so I re-wrote it. If anyone needs it, it's [here](https://gist.github.com/savandriy/237f8ffd7cccdf00e0d4a72c4b6cc383). For anyone still looking for the solution, you should check out my answer [below](https://stackoverflow.com/a/59324171/5952509). – Andriy Stolyar Dec 13 '19 at 14:16
2

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>
joseantgv
  • 1,943
  • 1
  • 26
  • 34
2
$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();
  }
});
1

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.

0

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.

JavaKungFu
  • 1,264
  • 2
  • 11
  • 24
  • 1
    I need both the scrollbar. Am talking about the behavior. When I scroll using my mouse trackball on reaching the end of the div's scroll it must not scroll the whole page. – Jeevan Apr 18 '12 at 14:16
0

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;
        }
    }
}
d.breve
  • 127
  • 1
  • 12