Detect mousemove when over an iframe?
Asked Answered
D

13

42

I have an iframe that takes up the entire window (100% wide, 100% high), and I need the main window to be able to detect when the mouse has been moved.

Already tried an onMouseMove attribute on the iframe and it obviously didn't work. Also tried wrapping the iframe in a div like so:

<div onmousemove="alert('justfortesting');"><iframe src="foo.bar"></iframe></div>

.. and it didn't work. Any suggestions?

Dodge answered 13/4, 2011 at 6:54 Comment(1)
I'm not 100% sure. It's been years. I think you need to capture the mouseover event in the child page - the page that loads into the frame.Terr
K
40

If your target isn't Opera 9 or lower and IE 9 or lower you can use css attribute pointer-events: none.

I found it the best way just to ignore iframe. I add class with this attribute to iframe in onMouseDown event and remove in onMouseUp event.

Works perfect for me.

Kentonkentucky answered 8/8, 2012 at 12:58 Comment(5)
This is a great, and I believe best, solution. Only current drawback is that IE does not yet support the pointer-event attribute. caniuse.com/#search=pointer-eventsHanafee
Is there any way for this to work but for the iframe to still be interactive?Mainstream
Only problem with this is you can't interact with the iframe in any way with pointer events set to none, so for a lot of use cases this won't workVevay
Wow. I spent hours on this. To think it was THAT easy. Thanks so much!Kurgan
Just to clarify, the pointer-events attribute is added to the iframe style like <iframe src="iframe-path.html" style="height: 400px; width=400px; pointer-events: none;"></iframe>Fibroma
A
29

Iframes capture mouse events, but you can transfer the events to the parent scope if the cross-domain policy is satisfied. Here's how:

// This example assumes execution from the parent of the the iframe

function bubbleIframeMouseMove(iframe){
    // Save any previous onmousemove handler
    var existingOnMouseMove = iframe.contentWindow.onmousemove;

    // Attach a new onmousemove listener
    iframe.contentWindow.onmousemove = function(e){
        // Fire any existing onmousemove listener 
        if(existingOnMouseMove) existingOnMouseMove(e);

        // Create a new event for the this window
        var evt = document.createEvent("MouseEvents");

        // We'll need this to offset the mouse move appropriately
        var boundingClientRect = iframe.getBoundingClientRect();

        // Initialize the event, copying exiting event values
        // for the most part
        evt.initMouseEvent( 
            "mousemove", 
            true, // bubbles
            false, // not cancelable 
            window,
            e.detail,
            e.screenX,
            e.screenY, 
            e.clientX + boundingClientRect.left, 
            e.clientY + boundingClientRect.top, 
            e.ctrlKey, 
            e.altKey,
            e.shiftKey, 
            e.metaKey,
            e.button, 
            null // no related element
        );

        // Dispatch the mousemove event on the iframe element
        iframe.dispatchEvent(evt);
    };
}

// Get the iframe element we want to track mouse movements on
var myIframe = document.getElementById("myIframe");

// Run it through the function to setup bubbling
bubbleIframeMouseMove(myIframe);

You can now listen for mousemove on the iframe element or any of its parent elements -- the event will bubble up as you would expect.

This is compatible with modern browsers. If you need it to work with IE8 and below, you'll need to use the IE-specific replacements of createEvent, initMouseEvent, and dispatchEvent.

Acetanilide answered 10/3, 2013 at 2:44 Comment(1)
See my newest answer for more info regarding deprecation & how to deal w/ itRecitativo
W
19

Another way to solve this that work well for me is to disable mouse move events on the iframe(s) with something like on the mouse down:

$('iframe').css('pointer-events', 'none');

and then, re-enable mouse move events on the iframe(s) on the mouse up:

$('iframe').css('pointer-events', 'auto');

I tried some of the other approaches above and they work, but this seems to be the simplest approach.

Credit to: https://www.gyrocode.com/articles/how-to-detect-mousemove-event-over-iframe-element/

Whimsicality answered 18/5, 2018 at 18:42 Comment(1)
Very simple solution that worked for me. Worth trying.Newsmonger
R
10

MouseEvent.initMouseEvent() is now deprecated, so @Ozan's answer is a bit dated. As an alternative to what's provided in his answer, I'm now doing it like this:

var bubbleIframeMouseMove = function( iframe ){

    iframe.contentWindow.addEventListener('mousemove', function( event ) {
        var boundingClientRect = iframe.getBoundingClientRect();

        var evt = new CustomEvent( 'mousemove', {bubbles: true, cancelable: false})
        evt.clientX = event.clientX + boundingClientRect.left;
        evt.clientY = event.clientY + boundingClientRect.top;

        iframe.dispatchEvent( evt );

    });

};

Where I'm setting clientX & clientY you'll want to pass any info from the content window's event to the event we'll be dispatching (i.e., if you need to pass something like screenX/screenY, do it there).

Recitativo answered 18/7, 2016 at 17:11 Comment(5)
a good start, but incomplete. Not all relevant data is copied from the event captured on the iframe to the generated clone. One thing missing is evt.buttons. Looking at how many keys a MouseEvent has, I'm almost certain that this is not the only thing missing. By running this solution side by side with Jagi's solution, one should be able to 'fill in the gaps' step by step.Isocyanide
maybe loop through all keys using Object.keys, and do standard cloning, and then do by hand only those keys where something changes, such as .clientX, .clientY as in the solution.Isocyanide
I'm using this solution with one line added, namely evt.buttons = event.buttons, and it's working for what I'm doing. For other use cases, you probably have to copy more, as I said.Isocyanide
IE doesn't have CustomEvent, but there's a polyfill, see developer.mozilla.org/en-US/docs/Web/API/CustomEvent/…Isocyanide
@dieterh glad to hear itRecitativo
F
8

The page inside your iframe is a complete document. It will consume all events and have no immediate connection to it's parent document.

You will need to catch the mouse events from javascript inside the child document and then pass this somehow to the parent.

Flyn answered 13/4, 2011 at 7:2 Comment(1)
Any ideas how to pass up the mouse coordinates properly?Mansized
A
7

I have been faced with a similar issue, where I had div's that I wanted to drag around over an iFrame. Problem was that if the mouse pointer moved outside the div, onto the iFrame, the mousemove events were lost and the div stopped dragging. If this is the sort of thing you want to do (as opposed to just detecting the user waving the mouse over the iFrame), I found a suggestion in another question thread which seems to work well when I tried it.

In the page that contains the and the things you want to drag, also include a like this:

<div class="dragSurface" id="dragSurface">
<!-- to capture mouse-moves over the iframe-->
</div>

Set it's initial style to be something like this:

.dragSurface
{
  background-image: url('../Images/AlmostTransparent.png');
  position: absolute;
  z-index: 98;
  width: 100%;
  visibility: hidden;
}

The z-index of '98' is because I set the div's I want to drag around to be z-index:99, and the iFrame at z-index:0.

When you detect the mousedown in the to-be-dragged object (not the dragSurface div), call the following function as part of your event handler:

function activateDragSurface ( surfaceId )
{
  var surface = document.getElementById( surfaceId );
  if ( surface == null ) return;

  if ( typeof window.innerWidth != 'undefined' )
  { viewportheight = window.innerHeight; } 
  else
  { viewportheight = document.documentElement.clientHeight; }

  if ( ( viewportheight > document.body.parentNode.scrollHeight ) && ( viewportheight > document.body.parentNode.clientHeight ) )
  { surface_height = viewportheight; }
  else
  {
    if ( document.body.parentNode.clientHeight > document.body.parentNode.scrollHeight )
    { surface_height = document.body.parentNode.clientHeight; }
    else
    { surface_height = document.body.parentNode.scrollHeight; }
  }

  var surface = document.getElementById( surfaceId );
  surface.style.height = surface_height + 'px';
  surface.style.visibility = "visible";
}

Note: I cribbed most of that from somebody else's code I found on the internet! The majority of the logic is just there to set the size of the dragSurface to fill the frame.

So, for example, my onmousedown handler looks like this:

function dragBegin(elt)
{
  if ( document.body.onmousemove == null )
  {
    dragOffX = ( event.pageX - elt.offsetLeft );
    dragOffY = ( event.pageY - elt.offsetTop );
    document.body.onmousemove = function () { dragTrack( elt ) };
    activateDragSurface( "dragSurface" ); // capture mousemoves over the iframe.
  }
}

When dragging stops, your onmouseup handler should include a call to this code:

function deactivateDragSurface( surfaceId )
{
  var surface = document.getElementById( surfaceId );
  if ( surface != null ) surface.style.visibility = "hidden";
}

Finally, you create the background image (AlmostTransparent.png in my example above), and make it anything except completely transparent. I made an 8x8 image with alpha=2.

I have only tested this in Chrome so far. I need to get it working in IE as well, and will try and update this answer with what I discover there!

Allout answered 19/3, 2012 at 3:44 Comment(1)
This worked perfectly for me. Also, I referenced a somewhat simplified strategy for the div overlay from this postFoggia
C
6

I found a relatively simple solution to this for a similar issue I was having where I wanted to resize the iframe and a long with a div sitting next to it. Once the mouse went over the iframe jquery would stop detecting the mouse.

To fix this I had a div sitting with the iframe both filling the same area with the same styles except for the z-index of the div (set to 0) and the iframe (set to 1). This allowed for the iframe to be used normally when not resizing.

<div id="frameOverlay"></div>  
<iframe></iframe>

When resizing, the div z-index gets set to 2 and then back to 0 afterwards. This meant the iframe was still visible but the overlay was blocking it, allowing for the mouse to be detected.

Crownwork answered 20/4, 2014 at 6:52 Comment(1)
Borrowed from your idea. This is a great answer since you do not have to worry about bubbling events up to the parent from childAlex
C
5

On your "parent" frame, select your "child" iframe and detect the event you are interested, in your case mousemove

This an example of code to be used in your "parent" frame

document.getElementById('yourIFrameHere').contentDocument.addEventListener('mousemove', function (event) {
                console.log(, event.pageX, event.pageY, event.target.id);
            }.bind(this));
Carminecarmita answered 9/4, 2015 at 7:31 Comment(1)
Is this code right? Because I'm not getting data from the modal with iframe. Nothing happens, no error.Yurik
D
3
<script>
// dispatch events to the iframe from its container
$("body").on('click mouseup mousedown touchend touchstart touchmove mousewheel', function(e) {
    var doc = $("#targetFrame")[0].contentWindow.document,
        ev = doc.createEvent('Event');
    ev.initEvent(e.originalEvent.type, true, false);
    for (var key in e.originalEvent) {
        // we dont wanna clone target and we are not able to access "private members" of the cloned event.
        if (key[0] == key[0].toLowerCase() && $.inArray(key, ['__proto__', 'srcElement', 'target', 'toElement']) == -1) {
            ev[key] = e.originalEvent[key];
        }
    }
    doc.dispatchEvent(ev);
});
</script>
<body>
<iframe id="targetFrame" src="eventlistener.html"></iframe>
</body>
Dulcle answered 4/6, 2013 at 9:47 Comment(0)
D
1

Here is my solution with jQuery. You can detect multiple events as I do below. Putting the .on() event handler inside the .on('load') event handler is necessary, because it would stop detecting the iframe content events when the iframe navigates to a new page otherwise. Additionally, I believe this only works if the iframe content is on the same domain as the parent page due to security, and there is no way around that.

$(document).ready(function() {
    $('#iframe_id').on('load', function() {
        $('#iframe_id').contents().on('click mousemove scroll', handlerFunction);
    });
});

handlerFunction() {
    //do stuff here triggered by events in the iframe
}
Diondione answered 24/1, 2020 at 22:22 Comment(0)
R
1

Basic way in document

var IE = document.all?true:false;

// If NS -- that is, !IE -- then set up for mouse capture
if (!IE) document.captureEvents(Event.MOUSEMOVE);

// Set-up to use getMouseXY function onMouseMove
document.onmousemove = getMouseXY;

// Temporary variables to hold mouse x-y pos.s
var tempX = 0;
var tempY = 0;

// Main function to retrieve mouse x-y pos.s

function getMouseXY(e) {
  if (IE) { // grab the x-y pos.s if browser is IE
    tempX = event.clientX + document.body.scrollLeft;
    tempY = event.clientY + document.body.scrollTop;
  } else {  // grab the x-y pos.s if browser is NS
    tempX = e.pageX;
    tempY = e.pageY;
  }  
  // catch possible negative values in NS4
  if (tempX < 0){tempX = 0}
  if (tempY < 0){tempY = 0}  

  // show the position values in the form named Show
  // in the text fields named MouseX and MouseY
  console.log(tempX, tempY);
  return true;
}

Detect Mouse Move over Iframe

window.frames['showTime'].document.onmousemove = getMouseXY;

Detect Mouse Move over in Child Iframe under parent's Iframe

window.frames['showTime'].document.getElementById("myFrame").contentWindow.onmousemove = getMouseXY;

Trigger an IDLE detect function when no mouse move in Browser.

Rainger answered 19/1, 2021 at 9:22 Comment(0)
H
0

you can add a overlay on top of iframe when drag begin ( onmousedown event ) , and remove it when drag end ( on mouserup event ). the jquery UI.layout plugin use this method to solve this problem.

Hombre answered 13/7, 2013 at 11:57 Comment(0)
P
0

I improved @jeremy's code and added typescript support and works just fine:

/**
 * @param iframe Iframe HTML element
 * @param callback A function called after the `dispatchEvent`
 * @returns `cleanUp` A cleanUp function to remove the event listener
 */
export const iframeMouseMoveDispatcher = (
    iframe: HTMLIFrameElement,
    callback?: (e: IframeMouseMoveDispatcherEvent) => unknown
) => {
    const onMouseMoveEvent = (e: MouseEvent) => {
        const boundingClientReact = iframe.getBoundingClientRect();

        const customEvent = new CustomEvent<{
            clientX: number;
            clientY: number;
            button: number;
            buttons: number;
        }>("mousemove", {
            bubbles: true,
            cancelable: false,
            detail: {
                ...e,
                clientX: e.clientX + boundingClientReact.left,
                clientY: e.clientY + boundingClientReact.top,
            },
        });

        iframe.dispatchEvent(customEvent);
        callback?.(customEvent);
    };

    iframe.contentWindow?.addEventListener("mousemove", onMouseMoveEvent);

    return () => {
        iframe.contentWindow?.removeEventListener("mousemove", onMouseMoveEvent);
    };
};
Preferment answered 23/1 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.