68

I have a modal box window (pop-up) that contains an iframe,
and inside that iframe there's a div that is scrollable.

When I scroll the iframe's inner DIV, and it has reached its top or bottom limit,
the window of the browser itself starts to scroll. this is an unwanted behavior.

I've tried something like this, which kills the main window scroll when
onMouseEnter when mouse enters pop-up box area:

e.preventDefault() is not working as it should for some reason...

$("#popup").mouseenter(function(){
   $(window).bind("scroll", function(e){
        e.preventDefault();
   }); 
}).mouseleave(function(){
    $(window).unbind("scroll");
});

Update

Seems like now in 2013 e.preventDefault(); is enough...

vsync
  • 87,559
  • 45
  • 247
  • 317
  • @RoatinMarth - since nobody asked me when they had created the browser, then I am left to fix their bad UX myself. if it's not obvious why this issue is unwanted then I don't know what to tell you.. – vsync Mar 23 '14 at 22:19

15 Answers15

28

Sorry, as far as I'm aware it is impossible to cancel any kind of scroll event.

Both W3 and MSDN say:

Cancelable  No
Bubbles     No

I think you'll have to leave this up to browser authors to fix. Firefox (3.5 on Linux, anyway) seems to have a better behaviour for me: it only scrolls the parent if the child is already at the top/bottom end at the moment you start using the scrollwheel.

bobince
  • 498,320
  • 101
  • 621
  • 807
  • 4
    It can be done - and relatively easy: http://stackoverflow.com/questions/5802467/prevent-scrolling-of-parent-element – T4NK3R Apr 27 '11 at 13:09
  • Depending on your layout, you could set the container to position:fixed when the mouse enters the scrollable target and reset it back to whatever it was when the mouse leaves the scrollable target. But remember that touch devices cannot hover. – Quickredfox Jan 07 '13 at 22:52
  • Can cancel event in chrome and FF now. – FlavorScape Jul 10 '13 at 16:46
  • @FlavorScape how? Does event.preventDefault() work for scroll event? MSDN says otherwise... – Ozil Apr 19 '16 at 21:40
  • For older IE you have to do it differently. http://stackoverflow.com/questions/1000597/event-preventdefault-function-not-working-in-ie otherwise, remember that you're preventing it on the window. – FlavorScape Apr 19 '16 at 23:38
23

Solved (for some browsers) using a simple CSS property:
overscroll-behavior

body{
  height: 600px;
  overflow: auto;
}

section{
  width: 50%;
  height: 50%;
  overflow: auto;
  background: lightblue;
  overscroll-behavior: none; /*   <--- the trick    */
}

section::before{
  content: '';
  height: 200%;
  display: block;
}
<section>
 <input value='end' />
</section>

Simply apply that style property on the element which the scroll should be "locked-in" to and the scroll event will not bubble up to any parent element which might have a scroll as well.


Same demo as above but without the trick:

body{
  height: 600px;
  overflow: auto;
}

section{
  width: 50%;
  height: 50%;
  overflow: auto;
  background: lightblue;
}

section::before{
  content: '';
  height: 200%;
  display: block;
}
<section>
 <input value='end' />
</section>
Community
  • 1
  • 1
vsync
  • 87,559
  • 45
  • 247
  • 317
17

If we cannot prevent window scrolling, why not undo it? That is, catching the scroll event and then scrolling back to a fixed position.

The following code locks the Y-Axis as long as one hovers over $("#popup"):

// here we store the window scroll position to lock; -1 means unlocked
var forceWindowScrollY = -1;

$(window).scroll(function(event) {
  if(forceWindowScrollY != -1 && window.scrollY != forceWindowScrollY) {
    $(window).scrollTop(forceWindowScrollY);    
  }
});

$("#popup").hover(function() {
  if(forceWindowScrollY == -1) {
    forceWindowScrollY = $(window).scrollTop();
  }
}, function() {
  forceWindowScrollY = -1;
});

I use this for the query suggest box on http://bundestube.de/ (enter some characters into the top search box to make the scrollable pane visible):

Screenshot

This works flawlessly in Chrome/Safari (Webkit) and with some scrolling glitches in Firefox and Opera. For some reason, it does not work with my IE installation. I guess this has to do with jQuery's hover method, which appears to not work correctly in 100% of all cases.

Christian Kohlschütter
  • 2,914
  • 1
  • 16
  • 12
  • I tried your site and it doesn't work for me with Chrome / Firefox / Safari on Mac – zaius Jan 04 '11 at 23:28
  • That's strange. Works perfectly here on my Mac (10.6.6). Are we talking about the same thing? I have added a screenshot for clarity. The window's scrollbars are only disabled when the mouse pointer is over the suggest box. – Christian Kohlschütter Jan 08 '11 at 12:04
  • @zaius Could you please try again? – Christian Kohlschütter Jan 10 '11 at 14:18
  • Ah sorry - I was trying to scroll in the box on the results page. Yes, that is working for me now. Nice work! Don't know if it's intentional, but it doesn't seem to catch the upwards scroll as well. – zaius Jan 10 '11 at 23:44
  • @zaius Great that it's working for you. Yes, the upwards scroll is intended as is. I use an additional "if(window.scrollY > forceWindowScrollY)" condition. – Christian Kohlschütter Jan 11 '11 at 09:13
  • I've tried it on Firefox 24, Chrome 24 and IE9, and on all three it works but it's quite jerky. – AxeEffect Sep 30 '13 at 16:30
  • I'm using chart.js with this chart zoom plugin: https://github.com/chartjs/Chart.Zoom.js I wanted to zoom the chart without the page scrolling and this did the trick. Thank you! – 43Tesseracts Jul 10 '16 at 17:44
3

my jQuery plugin:

$('.child').dontScrollParent();

$.fn.dontScrollParent = function()
{
    this.bind('mousewheel DOMMouseScroll',function(e)
    {
        var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;

        if (delta > 0 && $(this).scrollTop() <= 0)
            return false;
        if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).height())
            return false;

        return true;
    });
}
psycho brm
  • 6,864
  • 1
  • 40
  • 42
  • seems to, at least the `this.on('mousewheel DOMMouseScroll', function(){})` part ( notice `on` instead of `bind` ) – Funkodebat Oct 14 '14 at 17:13
3

I know it's quite an old question, but since this is one of top results in google... I had to somehow cancel scroll bubbling without jQuery and this code works for me:

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

document.getElementById('a').onmousewheel = function(e) { 
  document.getElementById('a').scrollTop -= e. wheelDeltaY; 
  preventDefault(e);
}
Nebril
  • 832
  • 7
  • 17
  • 2
    @vsync of course it doesn't work, as the iframe in your example is cross domain, and you cannot get events for scrolling and stuff on a cross domain iframe. – markasoftware Jul 22 '13 at 17:25
2

That's how I solved the problem:

I call the following when I open the popup:

$('body').css('overflow','hidden');

Then, when I close the popup I call this:

$('body').css('overflow','auto');

The popup is meant to be modal so no interaction is required with the underlying body

Works pretty well

Gianluca Ghettini
  • 9,354
  • 14
  • 63
  • 132
2

As of now in 2018 and onwards e.preventDefault is enough.

$('.elementClass').on("scroll", function(e){
    e.preventDefault();
 }); 

This will prevent scroll to parent.

Mustkeem K
  • 5,524
  • 1
  • 24
  • 38
0

Apparently, you can set overflow:hidden to prevent scrolling. Not sure how that'd go if the doc is already scrolled. I'm also on a mouseless laptop, so no scrolly wheel testing for me tonight :-) It's probably worth a shot though.

Dan F
  • 11,555
  • 3
  • 43
  • 68
  • i need it to scroll..(long text) the question doesn't revolve around that. – vsync Sep 22 '09 at 13:26
  • Yeah, my thinking is set overflow:hidden on the PARENT window. Not the iframe itself. Therefore, parent window is no longer scrollable, but the contents of the iframe are. – Dan F Sep 22 '09 at 13:30
  • 2
    well, that would make the scrollbar disappear suddenly, and things will shift to the side..not such a great idea – vsync Sep 22 '09 at 13:53
0

you can try jscroll pane inside the iframe to replace the default scroll.

http://www.kelvinluck.com/assets/jquery/jScrollPane/jScrollPane.html

I am not sure, but give it a try

adardesign
  • 29,076
  • 14
  • 59
  • 80
  • 1
    This is very interesting, thanks. I used this plugin before, its an ugly solution, but a good one for this difficult problem of mine. – vsync Sep 22 '09 at 15:15
0

Here's what I do:

  $('.noscroll').on('DOMMouseScroll mousewheel', function(ev) {
     var prevent = function() {
         ev.stopPropagation();
         ev.preventDefault();
         ev.returnValue = false;
         return false;
     }
     return prevent();
  }); 

demo fiddle

Use CSS overflow:hidden to hide the scrollbar as this will do nothing if they drag it.

Works cross-browser

parliament
  • 18,013
  • 35
  • 131
  • 223
  • 1
    makes no sense to have `prevent` function inside the cb function. if you must write you code using a `prevent` function, then take it outside so it could be used by any other even for DRY purposes, and pass it your `ev` argument. anyway, your answer has no new information or angle on the matter which was already solved. – vsync Dec 13 '14 at 23:24
0

New web dev here. This worked like a charm for me on both IE and Chrome.

static preventScrollPropagation(e: HTMLElement) {
    e.onmousewheel = (ev) => {
        var preventScroll = false;
        var isScrollingDown = ev.wheelDelta < 0;
        if (isScrollingDown) {
            var isAtBottom = e.scrollTop + e.clientHeight == e.scrollHeight;
            if (isAtBottom) {
                preventScroll = true;
            }
        } else {
            var isAtTop = e.scrollTop == 0;
            if (isAtTop) {
                preventScroll = true;
            }
        }
        if (preventScroll) {
            ev.preventDefault();
        }
    }
}

Don't let the number of lines fool you, it is quite simple - just a bit verbose for readability (self documenting code ftw right?)

Vivek Maharajh
  • 6,166
  • 3
  • 20
  • 27
  • what language is this? – vsync Mar 22 '15 at 12:04
  • 1
    TypeScript. As I said, new web dev - I didn't realize that JavaScript was the defacto language when I started and TypeScript seems pretty nice so far. It actually gets compiled to JavaScript to be used by browsers. – Vivek Maharajh Mar 23 '15 at 03:33
  • I believe the point of a coding language is not only to communicate with machines but also to be understood by as many humans as possible, and IMHO, **javascript** is by far a better choice than **typescript**. – vsync Mar 23 '15 at 16:16
  • 3
    Can you elaborate on why you think javascript is more human understandable than typescript? I'm new to web dev, but I'm a pretty experienced dev in general, and I've noticed that typed languages tend to be more readable than their un-typed ancestors. – Vivek Maharajh Mar 23 '15 at 17:50
  • I would have to agree with Vivek, typescript is even being made the main language to use with Angular2. – mjwrazor Sep 02 '16 at 17:57
  • @vivekmaharajh It's misleading to call un-typed languages "ancestors". Typed languages are **far** more common the more you go back in time. – Florian Wendelborn Feb 28 '17 at 05:43
  • @Dodekeract I should have just said "counterparts". I didn't mean to make any chronological claims :) – Vivek Maharajh Feb 28 '17 at 05:52
  • @vivekmaharajh You can still change it. :) – Florian Wendelborn Feb 28 '17 at 06:03
0

I would like to add a bit updated code that I found to work best:

var yourElement = $('.my-element');

yourElement.on('scroll mousewheel wheel DOMMouseScroll', function (e) {
    var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;

    if (delta > 0 && $(this).scrollTop() <= 0)
        return false;
    if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).outerHeight())
        return false;

    return true;
});

The difference between this one and one that is already mentioned above is the addition of more events and the usage of outerHeight() instead of height() to avoid crashing if element has padding!

0
$('.scrollable').on('DOMMouseScroll mousewheel', function (e) {
    var up = false;
    if (e.originalEvent) {
        if (e.originalEvent.wheelDelta) up = e.originalEvent.wheelDelta / -1 < 0;
        if (e.originalEvent.deltaY) up = e.originalEvent.deltaY < 0;
        if (e.originalEvent.detail) up = e.originalEvent.detail < 0;
    }

    var prevent = function () {
        e.stopPropagation();
        e.preventDefault();
        e.returnValue = false;
        return false;
    }

    if (!up && this.scrollHeight <= $(this).innerHeight() + this.scrollTop + 1) {
        return prevent();
    } else if (up && 0 >= this.scrollTop - 1) {
        return prevent();
    }
});
Roland Soós
  • 2,649
  • 4
  • 29
  • 45
0

Try the below code:

var container = document.getElementById('a');
container.onwheel = (e) => {
    const deltaY = e.wheelDeltaY || -(e.deltaY * 25); // Firefox fix
    container.scrollTop -= deltaY;
    e.preventDefault();
    e.stopPropagation();
    e.returnValue = false;
};
Mehdi Karamosly
  • 4,996
  • 1
  • 25
  • 46
JBeen
  • 306
  • 4
  • 6
-2
function stopPropogation(e)
{
    e = e || window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();
    if (e.preventDefault) e.preventDefault();
}

This should work.

fasih.rana
  • 1,565
  • 1
  • 13
  • 23