Skip to content

Instantly share code, notes, and snippets.

@Craga89
Last active September 4, 2019 07:14
Show Gist options
  • Save Craga89/8219459 to your computer and use it in GitHub Desktop.
Save Craga89/8219459 to your computer and use it in GitHub Desktop.
jQuery UI Draggable - Droppables in an iFrame
// Create new object to cache iframe offsets
$.ui.ddmanager.frameOffsets = {};
// Override the native `prepareOffsets` method. This is almost
// identical to the un-edited method, except for the last part!
$.ui.ddmanager.prepareOffsets = function (t, event) {
var i, j,
m = $.ui.ddmanager.droppables[t.options.scope] || [],
type = event ? event.type : null, // workaround for #2317
list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(),
doc, frameOffset;
droppablesLoop: for (i = 0; i < m.length; i++) {
//No disabled and non-accepted
if (m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0], (t.currentItem || t.element)))) {
continue;
}
// Filter out elements in the current dragoged item
for (j = 0; j < list.length; j++) {
if (list[j] === m[i].element[0]) {
m[i].proportions().height = 0;
continue droppablesLoop;
}
}
m[i].visible = m[i].element.css("display") !== "none";
if (!m[i].visible) {
continue;
}
//Activate the droppable if used directly from draggables
if (type === "mousedown") {
m[i]._activate.call(m[i], event);
}
// Re-calculate offset
m[i].offset = m[i].element.offset();
// Re-calculate proportions (jQuery UI ~1.10 introduced a `proportions` cache method, so support both here!)
proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
typeof m[i].proportions === 'function' ? m[i].proportions(proportions) : (m[i].proportions = proportions);
/* ============ Here comes the fun bit! =============== */
// If the element is within an another document...
if ((doc = m[i].document[0]) !== document) {
// Determine in the frame offset using cached offset (if already calculated)
frameOffset = $.ui.ddmanager.frameOffsets[doc];
if (!frameOffset) {
// Calculate and cache the offset in our new `$.ui.ddmanager.frameOffsets` object
frameOffset = $.ui.ddmanager.frameOffsets[doc] = $(
// Different browsers store it on different properties (IE...)
(doc.defaultView || doc.parentWindow).frameElement
).offset();
}
// Add the frame offset to the calculated offset
m[i].offset.left += frameOffset.left;
m[i].offset.top += frameOffset.top;
}
}
};
@Craga89
Copy link
Author

Craga89 commented Jan 4, 2014

Note the $.ui.ddmanager.frameOffsets code that caches the [i]frame's offset in the document. If this changes regularly, you may need to prevent caching this and instead re-evaluate the .offset() call on the frameElement each time the .prepareOffsets() method is run.

@iva3682
Copy link

iva3682 commented Jan 13, 2014

не работает

@mikhail-eremin
Copy link

iFrameFix option for draggable/droppable does all that things from-the-box, i don't see any advantages of above
What will be really helpful is a fix that will help to calculate drop position correctly if there is a scroll inside iframe. For now, there is jquery ui bug http://bugs.jqueryui.com/ticket/9301, which is opened for now and they don't want to fix it before 2.0

@karlhenselin
Copy link

This almost fixes my issue!
Before your code, with iframe fix I could drag over my iframes, but I couldn't drop on them at all.
Now it drops onto my iframes!
But, my droppable code seems to get called once for each of my 3 drop targets. I am not sure why that is, or what I can do about it. I am guessing it is in the code here?

    m[i].proportions({ width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight });

Thanks for submitting this awesome code!

@karlhenselin
Copy link

I tested with the tolerance: "fit" option now, and it doesn't go into all 3 now, but they always go into the middle one, even if they are over one of the side ones instead.

@Craga89
Copy link
Author

Craga89 commented Jun 2, 2014

@mikhail-eremin, the iFrameFix option does not address the issue that this Gist provides a solution to, as taken from their description:

iFrameFix: Prevent iframes from capturing the mousemove events during a drag. Useful in combination with the cursorAt option, or in any case where the mouse cursor may not be over the
helper.

This Gist provides a mechanism for having droppables in a completely separate frame, but still allowing the parent frame's draggables to register them as allowed targets.

@Craga89
Copy link
Author

Craga89 commented Jun 2, 2014

@karlhenselin not sure what you mean. If you share a reduced test case on codepen.io or similar, we might be able to help.

@Craga89
Copy link
Author

Craga89 commented Jun 2, 2014

Updated code to clean up and fix some small issues with previous fix.

@TheGr8Nik
Copy link

Great work man!
I have forked your work, but I can't make an pull request.
I try to use your fix for more iframe drag and drop, but I found an issue: it uses always the offset of the first registered iframe. The problem is in the storing of the offsets. I have changed the '$.ui.ddmanager.frameOffsets[doc]' in '$.ui.ddmanager.frameOffsets[frame.id]' where frame is the frameElement. Give a look to my fork for see the code ;-)

@yezhiming
Copy link

Great!
save me a lot of time. Thanks.

@kamelkev
Copy link

kamelkev commented Jun 6, 2015

Hi Craig,

Can you specify what version of jQuery you targeted with this fix? It's not clear from your post.

thanks

EDIT: Author originally posted about this on his blog, here: http://blog.craigsworks.com/jquery-ui-draggable-droppables-in-an-iframe/

Specifically this fix was for jquery UI 1.8+

@lisardggY
Copy link

This is a great fix and saved me a lot of issues. There's just one further fix I made to it - in my app I drag elements both into an iframe from the main doc, and within the iframe itself. The current code will apply the offset fix to elements dragged inside the iframe, causing an over-correction to the other side.

The solution is simple - instead of checking whether the droppable is in a different document than the main doc, compare it to the draggable element's document:

Replace line 48 with this:

 var draggableDocument = t.element[0].ownerDocument;
 if ((doc = m[i].document[0]) !== draggableDocument) {

@noc2spam
Copy link

Some times I get this weird error

Uncaught TypeError: Cannot read property 'ddmanager' of undefined

Anybody has fix?

@vsarangan
Copy link

Hi, I have some issues in droppable for example i have a iframe that is having inside mouse move event and parent page having draggable elements after using iframefix true, i am able to over the inside iframe droppable area but mouse move event not working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment