Pass mousewheel event through fixed content
Asked Answered
S

5

17

The best way to understand this is to look at this fiddle.

Notice how mouse wheel over the fixed content in the red box does nothing. I would like the scrollable div to scroll.

In case the fiddle dies - basically I have a scrollable div with a fixed element over it. Typically when you mouse wheel over a scrollable div it will of course scroll. But if you are over the fixed element instead then no scroll happens. Depending on your site layout this could be counter intuitive to a user.

jQuery solutions are okay.

Smattering answered 24/8, 2011 at 21:27 Comment(4)
Please modify the question to give more detail about what you want to accomplish. (For example, I assume you are expecting the div with the tall content to scroll when you use the mouse wheel over the fixed content, but it would help if you said that in your question.)Hesson
Yes. That is what I meant. As that is the only scrollable object in the screen I thought it was obvious.Smattering
I think it is reasonably obvious for getting your question answered; less so for future StackOverflow users who might benefit.Hesson
That makes sense. Especially if my fiddle goes away eventually. I updated my question to be explicit.Smattering
N
5

I think this does what you're asking for!

$('#fixed').bind('mousewheel', function(e){
     var scrollTo= (e.wheelDelta*-1) + $('#container').scrollTop();
    $("#container").scrollTop(scrollTo);
});

EDIT: Updated the jsFiddle link to one that actually works
DOUBLE EDIT: Best to dispense with the .animate() on further testing...
jsFiddle Example

TRIPLE EDIT: Much less pretty (and will probably be horribly slow with a lot of elements on the page), but this works and I owe a lot to this stackoverflow answer.

$('#fixed').bind('mousewheel', function(e) {


var potentialScrollElements = findIntersectors($('#fixed'), $('*:not(#fixed,body,html)'));
$.each(potentialScrollElements, function(index, Element) {
    var hasVerticalScrollbar = $(Element)[0].scrollHeight > $(Element)[0].clientHeight;
    if (hasVerticalScrollbar) {
        var scrollTo = (e.wheelDelta * -1) + $(Element).scrollTop();
        $(Element).scrollTop(scrollTo);
    }
});
});


function findIntersectors(targetSelector, intersectorsSelector) {
var intersectors = [];

var $target = $(targetSelector);
var tAxis = $target.offset();
var t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
var t_y = [tAxis.top, tAxis.top + $target.outerHeight()];

$(intersectorsSelector).each(function() {
    var $this = $(this);
    var thisPos = $this.offset();
    var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
    var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];

    if (t_x[0] < i_x[1] && t_x[1] > i_x[0] && t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
        intersectors.push($this);
    }

});
return intersectors;

}

Nickel answered 24/8, 2011 at 21:52 Comment(3)
That looks promising. Do you suppose there is a way without explicitly knowing what element lies beneath it.Smattering
Note to others, this doesn't work in FF. FF apparently likes the DOMMouseWheel event that has different properties. So take Touk's answer, bind it to dommousewheel also. See link. help.dottoro.com/ljqeknfl.phpSmattering
I needed to change e.wheelDelta for e.originalEvent.wheelDelta for it to workFranciscafranciscan
N
10

A much, MUCH simpler, but much less widely supported, answer is the following:

#fixed{ pointer-events:none; }

jsFiddle
Doesn't work in IE at all though unfortunately! But you could use modernizr or somesuch to detect whether it was supported and use the jQuery as a stop-gap where it isn't.

Courtesy of Mr. Dominic Stubbs

Nickel answered 25/8, 2011 at 10:4 Comment(4)
+1 That is a great suggestion Touk. Too bad I will have to wait a decade before it is standard in IE and everyone has migrated.Smattering
but this also disables clicking, say if the fixed element is a button to scroll to topCeaseless
This seems to be the only solution if you want to scroll vertically and HORIZONTALLY as I do in my situationTonetic
FYI there's pointer-events: none polyfills for IE. Here's one: github.com/kmewhort/pointer_events_polyfillUhl
R
9

I had this problem and this works for me (using jquery):

$(document).ready( function (){
    $('#fixed').on('mousewheel',function(event) {
        var scroll = $('#container').scrollTop();
        $('#container').scrollTop(scroll - event.originalEvent.wheelDeltaY);
        return true;
    });
});

Works on Safari and Chrome: http://jsfiddle.net/5bwWe/36/

Rollmop answered 20/5, 2013 at 23:5 Comment(1)
This is brilliant! Thank you so much. FYI, you can get this to work in Firefox as well by binding to the DOMMouseScroll event and capturing the value of event.originalEvent.detail – see here: #8886781Schreiber
N
5

I think this does what you're asking for!

$('#fixed').bind('mousewheel', function(e){
     var scrollTo= (e.wheelDelta*-1) + $('#container').scrollTop();
    $("#container").scrollTop(scrollTo);
});

EDIT: Updated the jsFiddle link to one that actually works
DOUBLE EDIT: Best to dispense with the .animate() on further testing...
jsFiddle Example

TRIPLE EDIT: Much less pretty (and will probably be horribly slow with a lot of elements on the page), but this works and I owe a lot to this stackoverflow answer.

$('#fixed').bind('mousewheel', function(e) {


var potentialScrollElements = findIntersectors($('#fixed'), $('*:not(#fixed,body,html)'));
$.each(potentialScrollElements, function(index, Element) {
    var hasVerticalScrollbar = $(Element)[0].scrollHeight > $(Element)[0].clientHeight;
    if (hasVerticalScrollbar) {
        var scrollTo = (e.wheelDelta * -1) + $(Element).scrollTop();
        $(Element).scrollTop(scrollTo);
    }
});
});


function findIntersectors(targetSelector, intersectorsSelector) {
var intersectors = [];

var $target = $(targetSelector);
var tAxis = $target.offset();
var t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
var t_y = [tAxis.top, tAxis.top + $target.outerHeight()];

$(intersectorsSelector).each(function() {
    var $this = $(this);
    var thisPos = $this.offset();
    var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
    var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];

    if (t_x[0] < i_x[1] && t_x[1] > i_x[0] && t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
        intersectors.push($this);
    }

});
return intersectors;

}

Nickel answered 24/8, 2011 at 21:52 Comment(3)
That looks promising. Do you suppose there is a way without explicitly knowing what element lies beneath it.Smattering
Note to others, this doesn't work in FF. FF apparently likes the DOMMouseWheel event that has different properties. So take Touk's answer, bind it to dommousewheel also. See link. help.dottoro.com/ljqeknfl.phpSmattering
I needed to change e.wheelDelta for e.originalEvent.wheelDelta for it to workFranciscafranciscan
E
5

UPDATE (August 2016): It seems the browser implementations have changed and it's no longer possible to re-dispatch a WheelEvent on a different target. See the discussion here.

For an alternative solution that should work across platforms, try this:

var target = $('#container').get(0);
$('#fixed').on('wheel', function (e) {
  var o = e.originalEvent;
  target.scrollTop += o.deltaY;
  target.scrollLeft += o.deltaX;
});

Working example: https://gist.run/?id=6a8830cb3b0564e7b16a4f31a9405386


Original answer below:

Actually, the best way to do it is to copy the original event. I've tried @Tuokakouan's code but scrolling behaves strangely (too fast) when we use a multitouch touchpad that has inertia. Here's my code:

var target = $('#container').get(0);

$('#fixed').on('wheel', function(e){
  var newEvent = new WheelEvent(e.originalEvent.type, e.originalEvent);
  target.dispatchEvent(newEvent);
});

You can try it here: http://jsfiddle.net/NIXin/t2expL6u/1/

What I'm trying to do now is also to pass the touch events, without much success. Since mobile phones and touch screens are now more popular, some people might want to scroll using their fingers instead - neither of the answers offered solves that.

Extortionate answered 5/12, 2014 at 14:57 Comment(4)
Your solution actually corrects scrolling too fast. Maybe a clean way would be to detect if the event comes from a touchpad or a mouse...Coolidge
I tried the fiddle but no current version of Chrome, FireFox nor IE works.Algetic
@Algetic I've updated my answer to reflect the current state of web.Extortionate
Works perfectly! Thanks! Btw if it's the body that should be scrolling, just use var target = $('body').get(0);Uhl
S
0

Well,all solutions with js are kind of delayed when scrolling on it. if the fixed element you use is just for display, then I have a good css tricks to achieve that.

make the fixed element z-index:-1 and the container element background-color:transparent here is the jsfiddle you can see: https://jsfiddle.net/LeeConan/4xz0vcgf/1/

Sputum answered 26/8, 2016 at 1:51 Comment(1)
this makes any content below the fixed element go above itHaland

© 2022 - 2024 — McMap. All rights reserved.