prevent Scroll bubbling from element to window

JavascriptJqueryHtmlEventsScroll

Javascript Problem Overview


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

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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

> auto The default scroll overflow behavior occurs as normal.

> contain Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.

> none No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.

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>

Solution 3 - Javascript

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.

Solution 4 - Javascript

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);
}

Solution 5 - Javascript

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;
	});
}

Solution 6 - Javascript

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

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

This will prevent scroll to parent.

Solution 7 - Javascript

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

Solution 8 - Javascript

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.

Solution 9 - Javascript

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

Solution 10 - Javascript

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

Solution 11 - Javascript

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?)

Solution 12 - Javascript

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!

Solution 13 - Javascript

$('.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();
    }
});

Solution 14 - Javascript

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;
};

Solution 15 - Javascript

function stopPropogation(e)
{
	e = e || window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
    if (e.preventDefault) e.preventDefault();
}

This should work.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvsyncView Question on Stackoverflow
Solution 1 - JavascriptbobinceView Answer on Stackoverflow
Solution 2 - JavascriptvsyncView Answer on Stackoverflow
Solution 3 - JavascriptChristian KohlschütterView Answer on Stackoverflow
Solution 4 - JavascriptNebrilView Answer on Stackoverflow
Solution 5 - Javascriptpsycho brmView Answer on Stackoverflow
Solution 6 - JavascriptMustkeem KView Answer on Stackoverflow
Solution 7 - JavascriptGianluca GhettiniView Answer on Stackoverflow
Solution 8 - JavascriptDan FView Answer on Stackoverflow
Solution 9 - JavascriptadardesignView Answer on Stackoverflow
Solution 10 - JavascriptparliamentView Answer on Stackoverflow
Solution 11 - JavascriptV MaharajhView Answer on Stackoverflow
Solution 12 - JavascriptAleksandar IvanovView Answer on Stackoverflow
Solution 13 - JavascriptRoland SoósView Answer on Stackoverflow
Solution 14 - JavascriptJBeenView Answer on Stackoverflow
Solution 15 - Javascriptfasih.ranaView Answer on Stackoverflow