React-dnd: Use inside and/or with an iframe.

Created on 20 Jul 2015  ·  22Comments  ·  Source: react-dnd/react-dnd

When context, source and target are inside an iframe DnD fails to unshift. I've tracked it back to the HTML5.js backend that comes with DnD.
Although I think it could be something to do with a rerender in the iframe. Here's an example on codepen http://codepen.io/mattconde/pen/zGLXML?editors=101

enhancement wontfix

Most helpful comment

After a lot of trying I managed to drop elements inside of an iframe like this:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import FrameComponent from 'react-frame-component';

class DragDropAwareIFrame extends Component {
  static propTypes = {
    innerRef: PropTypes.func,
  };

  static contextTypes = {
    dragDropManager: PropTypes.object.isRequired,
  }

  constructor(props, context) {
    super(props, context);

    this.manager = this.context.dragDropManager;
  }

  componentDidMount() {
    const iframe = ReactDOM.findDOMNode(this.iframe);
    this.manager.getBackend().addEventListeners(iframe.contentWindow);
  }

  componentWillUnmount() {
    const iframe = ReactDOM.findDOMNode(this.iframe);
    this.manager.getBackend().removeEventListeners(iframe.contentWindow);
  }

  handleRef = (el) => {
    this.iframe = el;
    if (this.props.innerRef) this.props.innerRef(el);
  }

  render() {
    const { innerRef, ...props } = this.props;
    return <FrameComponent {...props} ref={this.handleRef} />;
  }
}

<DragDropContextProvider backend={HTML5Backend}>
    <div>
        <Item />
        <DragDropAwareIFrame>
            <div>
                <Dustbin />
            </div>
        </DragDropAwareIFrame>
    </div>
</DragDropContextProvider>

Does this seem to be the right way of doing it?

All 22 comments

Out of the box dragging/dropping across frames didn't work for me either. It seems like drag/drop events just need to be applied to each additional frame's window object. Currently only the window object in which you're running your react app in gets them:

https://github.com/gaearon/react-dnd/blob/master/src/backends/HTML5.js#L130-L139

I monkey-patched the HTML5-backend in my recent project to make it work (setup and teardown accepting an alternate win window object):

https://gist.github.com/rasmusfl0e/39db428399bd196ef706

It also requires the backend instance to be exposed somehow (more monkey-patching in DragDropContext.js...) to execute setup and teardown when needed (in setting up your frames).

But once that's done - it works like a charm :D

Oh wow nice find, makes a lot of sense. Going to be giving it a try later today, can the change in DragDropContext.js be avoided? That way we could just make up a new backend like the docs mention.

I'm happy to consider a specific API and implementation proposal addressing this.
Unfortunately it's hard to digest from the gist what exactly was changed and why.

For now, let's say we don't support iframes.

@gaearon I am trying to build a drag n drop page builder. Tried everything else and they are horrible to work with flux. Can give me some clues where to tweak to make it work from cross frame?

@nadimtuhin I was working on a cross frame page builder myself when I raised the issue, and I'll be starting again on getting a multiple window solution working this week. Are you managing the iframe with react?

For me I'm not sure whether working inside iframes would be the best solution any more, it sure would be nice. But having the aim of getting react-dnd to work across multiple apps looks to be the better option.

I've had a brief look into it yesterday and just logging some of the handles in the html5 backend. and the source from the first window is being detected on the target from the second. But that's as far as I got.

@gaearon Some pointers/clues as where to look would be great, looking to make/attempt my first real open source contribs.

@mattconde no I am not managing iframe inside my react app. I plan on having two different apps on two different frames. The parent window holds the logic for data manipulation and the child frame for presentation. The child gets its props from parent flux store.

@mattconde

It's hard for me to give any pointers beside "try putting logs into HTML5 backend and then start tweaking it". Take the simplest possible example and see if you can make it work. The backend only does one thing: subscribes to window events and node events, and translates them to actions. I don't know enough about how iframes work. Maybe you need to change the code to subscribe not to the window, but to specific node's top level window (or iframe) if this makes sense. But I'm way out of my depth here so I may be talking rubbish.

@mattconde @gaearon

I had the same issue recently. The reason it fails is because HTML5Backend sets event listeners on window, but if you'd want to use it inside iframe, you should rather set them on, say, window.frames[0].

@gaearon What do you think about an option to pass context to setup/teardown function in HTML5Backend.js?

I've been hitting the same issue, by building an enhanced editor (text, images, ..) which works within an iframe and I need to drag & drop elements (text, img) within it, basically drag&drop the elements to change the order.

I've opened an issue https://github.com/gaearon/react-dnd/issues/435 and just figured out it was because of the iframe. I'm gonna try to "tweak it" but if there is any workaround I'd be glad to know! :)

I'm trying to make a PR to make it work with iframes. Currently in progress, it doesn't work yet, I'm having troubles figuring out how to invoke setup and teardown, respectivelly in my componentDidMount and componentWillUnmount methods.

https://github.com/gaearon/react-dnd-html5-backend/pull/27

@Vadorequest Sounds like we both are aiming for similar end product. I didn't end up getting it to work with react-dnd from parent to child window. The work around I settled on was having my editor (parent) app and having a preview (child) app. The preview app didn't render until it could connect to the editors redux store.

Then in my redux store I had a list of templates that I want the user to drag from the editor to great a content page. So my template list sat in the Editor window, but I'd want to drag them over to the Preview.

Thanks to react-dnd supporting native types, I could set up text drags in the Editor window to pass over the information needed for each template to be rendered. My code snippet...

    componentDidMount() {
        this.refs.template.addEventListener('dragstart', this.handleDragStart, false);
    },
    componentWillUnmount() {
        this.refs.template.removeEventListener('dragstart', this.handleDragStart, false);
    },
    handleDragStart(e) {
        const { template } = this.props;
        e.dataTransfer.setData('text/plain', JSON.stringify({
            type: template.type,
            name: template.name,
        }));
        return e;
    },

Then I could catch that JSON string the other end with react-dnd, works really well.

Let me know how you get on, would love to hear about alternative ways around it.

@mattconde I'm working on two separate branches, an ugly fix and a real fix for this. The ugly fix simply changes those lines in the HTML5Backend.js file. Changes can be viewed here: https://github.com/Gutenberg-Technology/react-dnd-html5-backend/commit/8b3d6a44b2cdbfe8d5bac025fc263715968d8b6c

_(it basically just add a target param to setup and teardown, which has my iframe as default value and fall back to using window. The fix is so ugly that it would actually fail if no iframe is found, but it'll do for now.)_

But this will likely fail on some situation, if there is more than one iframe, so it's just to avoid getting stuck. A better solution would be to manually call setup and teardown within my own code by just passing a target parameter to bind and unbind listeners.

Thanks for explaining your solution, I'll definitely improve my PR if I found a better workaround in a close future. :)

Hey guys, just ran into this problem myself, can't figure out how to connect window with iframe. For example I want to have some DragSource on window and I want to create some DropTarget on iframe, even if I tweak HTML5Backend I can't get it to work like that...

@mattconde Thanks for sharing this. I am building a similar end product I think. The worse thing is that we are using Angular 2. I ported react-dnd to Angular partially, but finally, I hit the wall with iframe. Going to try your way.

@gaearon The PRs here ☝️ allow for react-dnd to support iframes. If you have any concerns or need me to change anything let me know.

@kesne Are you the new maintainer? Can you review these PRs?

Hi @darthtrevino
Do your changes allow to drag an element from the page to a Frame?
I can drag and drop inside the Frame and outside.

Hey @crysislinux, I'm looking to port this library over to Angular 2 and would love to see what you guys came up with. Would you be willing to share your code? Thanks!

is there a way to drop items that are outside the iframe inside the iframe for example:

<DragDropContextProvider backend={HTML5Backend}>
    <div>
        <Item />
        <Frame>
            <div>
                <Dustbin />
            </div>
        </Frame>
    </div>
</DragDropContextProvider>

After a lot of trying I managed to drop elements inside of an iframe like this:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import FrameComponent from 'react-frame-component';

class DragDropAwareIFrame extends Component {
  static propTypes = {
    innerRef: PropTypes.func,
  };

  static contextTypes = {
    dragDropManager: PropTypes.object.isRequired,
  }

  constructor(props, context) {
    super(props, context);

    this.manager = this.context.dragDropManager;
  }

  componentDidMount() {
    const iframe = ReactDOM.findDOMNode(this.iframe);
    this.manager.getBackend().addEventListeners(iframe.contentWindow);
  }

  componentWillUnmount() {
    const iframe = ReactDOM.findDOMNode(this.iframe);
    this.manager.getBackend().removeEventListeners(iframe.contentWindow);
  }

  handleRef = (el) => {
    this.iframe = el;
    if (this.props.innerRef) this.props.innerRef(el);
  }

  render() {
    const { innerRef, ...props } = this.props;
    return <FrameComponent {...props} ref={this.handleRef} />;
  }
}

<DragDropContextProvider backend={HTML5Backend}>
    <div>
        <Item />
        <DragDropAwareIFrame>
            <div>
                <Dustbin />
            </div>
        </DragDropAwareIFrame>
    </div>
</DragDropContextProvider>

Does this seem to be the right way of doing it?

@ethanve Just chanced upon this thread from a dupe issue — I built what you described. https://cormacrelf.github.io/angular-skyhook/

I am also having trouble with using dnd within an iframe (no parent interaction). Dragging works fine, but drop zones don't work anymore. What is the solution to make drop zones work within an iframe (just moving the item entirely inside the iframe).

I want to point out that it works with the Touch backend but not with the HTML5 backend.

Was this page helpful?
0 / 5 - 0 ratings