Position in iframes
I wrote a Chrome extension Saladict, an inline translator, which involved such requirement: When user makes a text selection, something will pop up nearby the cursor.
It looks simple at first view. Just listen to a mouseup
event and get clientX
and clientY
from it.
But there is a flaw in it - mouseup
events inside iframes won't bubble up to the top frame.
The solution is actually quite simple. If you know how to connect the dots.
iframe script injection
Using the all_frames
property in manifest.json
, a content script can run in all frames.
1 |
{ |
Mouse Event Detection
Now you can listen to mouseup
event in all iframes.
1 |
// selection.js |
Upload Cursor Coordinates
clientX
and clientY
of the mouse events that are triggered in iframes are coordinates within iframe windows. Upload these coordinates as offsets to the upper frame, then plus the iframe position you will get the cursor position
within the upper frame window.
On Chrome you can boldly use postMessage
.
1 |
// selection.js |
Add offsets
How does the upper frame know which iframe is sending coordinates? Well, the message
event contains the content window of the iframe. Use it to match the iframe element.
1 |
// selection.js |
iframe Dragging
Another requirement for Saladict is to drag an iframe panel.
Dragging 101
Before getting into iframe dragging. There are few basic ideas of implementing a draggable element.
One of the most common approaches is to listen to mousedown
, mousemove
and mouseup
events, which handle drag start, dragging and drag end. And apply the offsets to the element's left
and top
style
properties.
If this is your first time implementing this feature, you are likely to listen to mousemove
events of the element itself.
You can indeed get the correct result in the way. The problem is, if the curser moves a bit too fast and leaves the element, the dragging will stop. That's why you should listen to global mousemove
event instead.
Dragging with iframe
The theory behind iframe dragging is the same. Only the mouse events triggered in iframes will not bubble up to the upper frame. You need to wrap it up yourselves.
iframe Part
Drag start is triggered by a draggable element inside iframe. For better performance, dragging and drag end event listeners are attached in drag start and are detached in drag end.
Dragging event listener is required here because the mousemove
event of the upper frame breaks inside the iframe. We need to let upper frame know what is happening inside iframe.
1 |
// iframe.js |
Upper Frame Part
Use handleFrameMousemove
to handle the offsets from iframe.
1 |
// parent.js |
Demo
You can drag the iframe square below:
Browser Compatibility
As you can see, nothing fancy here, just passing coordinates around. So for older browsers, just use the old ways to communicate. You can also manipulate the values directly if they are same-origin.