taye / interact.js

JavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+)
http://interactjs.io/
MIT License
12.35k stars 784 forks source link

Interact.js not working with elements in iframes if they was created in parent window #883

Open kachurun opened 3 years ago

kachurun commented 3 years ago

Actual behavior

I have an iframe inserted into my main site and I need to create elements inside the iframe from the parent context. But interact.js doesn't work if I create new elements from the parent context and then insert them in the iframe. In a real application I cannot create nodes from an iframe document, because the elements are created by the framework (Riot.js) and I cannot control the creation of components.

Demo: https://jsfiddle.net/kachurun/bugqc5dk/79/

Problem on this line: https://github.com/taye/interact.js/blob/55d5d57c4f1ad2f728109bb839f1ed99a45d8ba1/packages/%40interactjs/utils/is.ts#L29

Because thing really not instance of _window.Element, but instance of win.window.Element. I'm not sure if I know the correct way to fix this, except to add a check to the parent window, or remove the DOM2 part of the condition.

System configuration

interact.js version: 1.10.3

taye commented 3 years ago

interact('.target', { context: iframe.contentDocument })

Docs need updating 🙂

kachurun commented 3 years ago

Yes, the documentation needs to be updated, but I found the context option in the source code (and wrote it in the jsfiddle example), but this does not solve the problem because the element itself was created outside the iframe and the is.element() function does not work with this case.

taye commented 3 years ago

Then you probably need https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptNode

kachurun commented 3 years ago

Updated example https://jsfiddle.net/kachurun/k17wzmbu/

The adoptNode does not help. importNode solves the problem, but if I can run the importNode, then I can just create the element in the right context. This is not always the case, because as a rule elements are created by a framework (riotjs, vuejs, react, any other)

DaPedro commented 3 years ago

Same problem: Created DOM elements via vuejs in a iframe and they aren't useable for interact.js. This happens in Chrome, in Firefox everything is all fine.

Fixed this with appending the dragable elements via vanilla js in the right iframe document.

Thanks for the fast feedback!

kachurun commented 3 years ago

Same problem: Created DOM elements via vuejs in a iframe and they aren't useable for interact.js. This happens in Chrome, in Firefox everything is all fine.

Fixed this with appending the dragable elements via vanilla js in the right iframe document.

Thanks for the fast feedback!

You can fix it by patch is.element() function on interact.js

semiaddict commented 2 years ago

Fixed this with appending the dragable elements via vanilla js in the right iframe document.

Hi @DaPedro ,

I am facing the same issue with a component that is teleported to an iframe in Vue3. I tried manually teleporting the element with vanilla JS, and even using adoptNode, but this doesn't seem to fix the issue in Chrome.

Here's my code:


const componentToInsert = this.$refs.el;
const doc = iframe.contentDocument;
doc.body.appendChild(doc.adoptNode(componentToInsert .$el));

componentToInsert.$el instanceof doc.defaultView.Element still returns false in Chrome, but returns true in FireFox.

Could you please let me know how you appended the element in the iframe ?

semiaddict commented 2 years ago

I ended up patching the is.element as proposed by @kachurun. Here's the code if anyone needs it:

// Override interact's is.element to workaround a bug
// when working with elements in an iframe.
import is from "@interactjs/utils/is";
is.element = function (thing) {
  if (!thing || typeof thing !== "object") {
    return false;
  }
  return thing.nodeType === 1 && typeof thing.nodeName === "string";
};

This overrides the function for the entire application.

semiaddict commented 2 years ago

~This seems to have been fixed in https://github.com/taye/interact.js/pull/943.~ Issue seems to still occur in v1.10.17

codextech commented 1 year ago

@taye thank you for answer interact('.target', { context: iframe.contentDocument })

This worked for me.