How to disable scrolling in outer elements?
Asked Answered
A

5

16

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.

Alyworth answered 13/2, 2012 at 5:59 Comment(2)
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.Alyworth
#7571870Trounce
A
5

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...

Alyworth answered 13/2, 2012 at 7:13 Comment(1)
Can you elaborate on your answer and explain what is happening. I am especially confused about the part with the -40Antenatal
M
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.

Miquelmiquela answered 15/5, 2014 at 10:41 Comment(2)
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 - codepen.io/rommguy/pen/EarVwvResolvable
Gyro, you code doesn't work properly with IE11 - page (doc) is jumping up and down when inside div scroll is used. Ugly.Aldin
V
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>
Vaughan answered 5/12, 2016 at 22:45 Comment(1)
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: developers.google.com/web/updates/2017/11/overscroll-behaviorLowman
M
0

As per the comment by @Wildhoney, the way to do this now is to use overscroll-behavior: contain.

.overscroll-contain {
  overscroll-behavior: contain;
}

#outerScroll {
  height: 150px;
  width: 200px;
  overflow: auto;
  background: red;
}

#innerScroll {
  height: 100px;
  width: 100px;
  background: blue;
  overflow: auto;
}

.large-child {
  height: 500px;
}
<div id="outerScroll">
  <div id="innerScroll" class="overscroll-contain">
    <div class="large-child"></div>
  </div>
  <div class="large-child"></div>
</div>
Manes answered 24/1 at 1:31 Comment(0)
H
-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";
};
Homo answered 13/2, 2012 at 6:46 Comment(2)
thanks but this is exactly what i said i wanted to avoid...css shifting my content around.Alyworth
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.Homo

© 2022 - 2024 — McMap. All rights reserved.