Vimeo iFrame Stealing Mouse Wheel Event on Firefox

2019-06-16 16:43发布

问题:

I made this example here: http://jsbin.com/pokahec/edit?html,output

// creates a global "addWheelListener" method
// example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } );
(function(window,document) {

var prefix = "", _addEventListener, onwheel, support;

// detect event model
if ( window.addEventListener ) {
    _addEventListener = "addEventListener";
} else {
    _addEventListener = "attachEvent";
    prefix = "on";
}

// detect available wheel event
support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
          document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
          "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox

window.addWheelListener = function( elem, callback, useCapture ) {
    _addWheelListener( elem, support, callback, useCapture );

    // handle MozMousePixelScroll in older Firefox
    if( support == "DOMMouseScroll" ) {
        _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
    }
};

function _addWheelListener( elem, eventName, callback, useCapture ) {
    elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
        !originalEvent && ( originalEvent = window.event );

        // create a normalized event object
        var event = {
            // keep a ref to the original event object
            originalEvent: originalEvent,
            target: originalEvent.target || originalEvent.srcElement,
            type: "wheel",
            deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
            deltaX: 0,
            deltaZ: 0,
            preventDefault: function() {
                originalEvent.preventDefault ?
                    originalEvent.preventDefault() :
                    originalEvent.returnValue = false;
            }
        };

        // calculate deltaY (and deltaX) according to the event
        if ( support == "mousewheel" ) {
            event.deltaY = - 1/40 * originalEvent.wheelDelta;
            // Webkit also support wheelDeltaX
            originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
        } else {
            event.deltaY = originalEvent.detail;
        }

        // it's time to fire the callback
        return callback( event );

    }, useCapture || false );
}

})(window,document);

You can test in Firefox that scroll event is fired, except when over vimeo iframe ( and I guess any iFrame )

Is there any solution to fire event on iframe ?

PS - I want to use this in a custom scrollbar

回答1:

This is basically by design. Your code should be completely unaware of what the user does inside an IFRAME (especially one from a different origin like YouTube - this is a part of the web's security architecture, as mandated by the Same Origin Policy.)

Now, even in the cross-origin case browsers can choose to let scrolling affect the frame's ancestor if the frame itself doesn't scroll. This scrolling should happen without any events firing on the top document - see Chrome's behaviour if you scroll to the bottom of this IFRAME and keep scrolling: http://jsfiddle.net/8cj0dofx/1/ HTML:

<iframe src="data:text/html,<body style='background:grey;height:550px'>Hello" seamless></iframe>
<div style="height:100px">Hello</div>

JS:

document.addEventListener('DOMMouseScroll', function(e){
    document.getElementsByTagName('div')[0].firstChild.data += ' ' + e.type
});

document.addEventListener('mousewheel', function(e){
    document.getElementsByTagName('div')[0].firstChild.data += ' ' + e.type
});

What you'll see is that when you have scrolled to the end of the IFRAME, the main document will scroll but no events will fire until the mouse is above the parent document.



回答2:

It looks like it's a bug in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1084121

So there may not be a straightforward way to handle this. But since the action has an effect even if it's not being dispatched, there's a workaround that can be used. It may not work in every situation, but it should cover many cases.

Instead of detecting wheel event, you detect scroll, and use a switch detecting if the mouse is clicked or not. If the window scrolls and the mouse isn't clicked, then it's most likely from the mousewheel. Other cases will be if you trigger it from a script, in which case this can be handled easily also.

One case you won't handle is when the window cannot scroll anymore, then you won't get the event.

It would look like this:

var mouseDown = false;

function handle_wheel() {
    if (!mouseDown) {
        document.getElementById("debug-textarea").value = document.getElementById("debug-textarea").value + ' wheel';
    } else {
        document.getElementById("debug-textarea").value = document.getElementById("debug-textarea").value + ' scroll';
    }
}

window.onscroll = handle_wheel;
window.onmousedown = function () {
    mouseDown = true;
}
window.onmouseup = function () {
    mouseDown = false;
}

http://jsfiddle.net/wu9y6yua/4/