15

I have a vertically-scrolling div within a page that also scrolls vertically.

When the child div is scrolled with the mouse wheel and reaches the top or bottom of the scroll bar, the page (body) begins to scroll. While the mouse is over the child div, I'd like the page (body) scroll to be locked.

This SO post (scroll down to the selected answer) demonstrates the problem well.

This SO question is essentially the same as mine, but the selected answer causes my page contents to noticeably shift horizontally as the scrollbar disappears and reappears.

I thought there might be a solution that leverages event.stopPropagation(), but couldn't get anything to work. In ActionScript, this kind of thing would be solved by placing a mousewheel handler on the child div that calls stopPropagation() on the event before it reaches the body element. Since JS and AS are both ECMAScript languages, I thought the concept might translate, but it didn't seem to work.

Is there a solution that keeps my page contents from shifting around? Most likely using stopPropagation rather than a CSS fix? JQuery answers are welcome as is pure JS.

Community
  • 1
  • 1
ericsoco
  • 20,453
  • 20
  • 89
  • 117
  • i actually came across the same solution as yours independently (tho with a slight difference -- yours using jQuery events, mine using pure JS events. at any rate, looks like your solution works great, thanks @mrtsherman. – ericsoco Feb 13 '12 at 07:00
  • http://stackoverflow.com/questions/7571370/jquery-disable-scroll-when-mouse-over-an-absolute-div/7571867#comment11664776_7571867 – mrtsherman Feb 13 '12 at 07:02

4 Answers4

4

here's what i ended up with. very similar to @mrtsherman's answer here, only pure JS events instead of jQuery. i still used jQuery for selecting and moving the child div around, though.

// earlier, i have code that references my child div, as childDiv

function disableWindowScroll () {
    if (window.addEventListener) {
        window.addEventListener("DOMMouseScroll", onChildMouseWheel, false);
    }
    window.onmousewheel = document.onmousewheel = onChildMouseWheel;
}

function enableWindowScroll () {
    if (window.removeEventListener) {
        window.removeEventListener("DOMMouseScroll", onArticleMouseWheel, false);
    }
    window.onmousewheel = document.onmousewheel = null;
}

function onChildMouseWheel (event) {
    var scrollTgt = 0;
    event = window.event || event;
    if (event.detail) {
        scrollTgt = -40 * event.detail;
    } else {
        scrollTgt = event.wheelDeltaY;
    }

    if (scrollTgt) {
        preventDefault(event);
        $(childDiv).scrollTop($(childDiv).scrollTop() - scrollTgt);
    }
}

function preventDefault (event) {
    event = event || window.event;
    if (event.preventDefault) {
        event.preventDefault();
    }
    event.returnValue = false;
}

i've noticed the scrolling doesn't match normal scrolling exactly; it seems to scroll a bit faster than without this code. i assume i can fix by knocking down wheelDeltaY a bit, but it's odd that it would be reported differently by javascript than it's actually implemented by the browser...

Community
  • 1
  • 1
ericsoco
  • 20,453
  • 20
  • 89
  • 117
  • Can you elaborate on your answer and explain what is happening. I am especially confused about the part with the -40 – Harsh Feb 24 '18 at 12:52
1

I usually do it with a small hack listening to the scroll event on the document: it resets the scroll height back to the original one - effectively freezing the document from scrolling but any inner element with overflow: auto will still scroll nicely:

var scrollTop = $(document).scrollTop();
$(document).on('scroll.scrollLock', function() {
  $(document).scrollTop(scrollTop);
});

and then when I'm done with the inner scroll lock:

$(document).off('scroll.scrollLock');

the .scrollLock event namespace makes sure I'm not messing with any other event listeners on scroll.

stefan
  • 427
  • 5
  • 12
  • Nice solution. I tried it with small differences, and it works great, except for IE 10, which executes both scroll events on the doc - the mouse one and the "reset" one. Here is a link to a working exmple - http://codepen.io/rommguy/pen/EarVwv – Gyro Mar 23 '15 at 21:13
  • Gyro, you code doesn't work properly with IE11 - page (doc) is jumping up and down when inside div scroll is used. Ugly. – Andrew Oct 28 '16 at 12:37
0

Although this is an old question, here is how I do it with jQuery. This allows you to scroll a list within an outer list, or you can change the outer list to the document to do what the OP asked.

window.scrollLockHolder = null;
function lockScroll(id){
    if (window.scrollLockHolder == null){
        window.scrollLockHolder = $('#' + id).scrollTop();
    }
    $('#' + id).on('scroll', function(){
        $('#' + id).scrollTop(window.scrollLockHolder);
    });
}
function unlockScroll(id){
    $('#' + id).off('scroll');
    window.scrollLockHolder = null;
}

And you can use it like this:

<ul onmousemove="lockScroll('outer-scroller-id')" onmouseout="unlockScroll('outer-scroller-id')">
    <li>...</li>
    <li>...</li>
</ul>
NinjaBeetle
  • 85
  • 1
  • 11
  • Just a heads up for today's developers. It's worth keeping an eye on the support for `overscroll-behaviour` as that's going to land soon, and prevent all of the unnecessary JS code. Instead we'll just have `overscroll-behavior: contain`. Read: https://developers.google.com/web/updates/2017/11/overscroll-behavior – Wildhoney May 03 '18 at 11:49
-2

what about this:

div.onmousemove = function() { // may be onmouseover also works fine
    document.body.style.overflow = "hidden";
    document.documentElement.style.overflow = "hidden";
};

div.onmouseout = function() {
    document.body.style.overflow = "auto";
    document.documentElement.style.overflow = "auto";
};
Kevin
  • 1,159
  • 11
  • 21
  • thanks but this is exactly what i said i wanted to avoid...css shifting my content around. – ericsoco Feb 13 '12 at 07:01
  • sorry for my mistake... and i have another idea, set `div.style.overflow = "hidden";` and `div.onmousewheel = function(event) { event.preventDefault(); // control the scroll offset by setting div.scrollTop dynamically }`. it just my idea and i have not implemented it, you can try it. – Kevin Feb 13 '12 at 08:11