React-dnd: DragPreview is positioned wrong when using translate3d on child nodes

Created on 19 Aug 2015  ·  5Comments  ·  Source: react-dnd/react-dnd

Hey!

We try to make an element draggable, which contains a translate3d css style at some point. Unfortunately this repositions the whole drag-layer. I do not really understand why, since the translate3d is not on the root element, but on a child node of it.

I assembled a very simple example, showing the problem to you. (To make it work, you need to place the ReactDnD.min.js in the "./scripts/react-dnd-1.1.4/dist" folder, since i did not find any CDN that hosts ReactDnD).

Thanks for help!

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>DragPreview with wrong position example</title>

        <script src="http://fb.me/react-with-addons-0.13.3.js"></script>
        <script src="http://fb.me/JSXTransformer-0.13.3.js"></script>
        <script src="scripts/react-dnd-1.1.4/dist/ReactDnD.min.js"></script>

        <style>
            .ReactDnDElement {
                background: red;
                overflow: hidden;
                width: 200px;
                height: 100px;
            }

            .ReactDnDElement .wrapper {
                display: flex;
                align-items: center;
                width: 600px;
                height: 100%;
                transform: translate3d(-275px, 0, 0);
            }

            .ReactDnDElement .wrapper div {
                width: 150px;
                height: 50%;
            }

            .ReactDnDElement .wrapper div.green { background: green; }
            .ReactDnDElement .wrapper div.blue { background: blue; }
            .ReactDnDElement .wrapper div.yellow { background: yellow; }
            .ReactDnDElement .wrapper div.magenta { background: magenta; }
        </style>
    </head>

    <body>
        <script type="text/jsx">
            /* ReactDnDApp */

            var ReactDnDApp = React.createClass({
                render: function() {
                    return (
                        <ReactDnDElement></ReactDnDElement>
                    );
                }
            });

            ReactDnDApp = ReactDnD.DragDropContext(ReactDnD.HTML5)(ReactDnDApp)


            /* ReactDnDElement */

            var ReactDnDElement = React.createClass({
                render: function() {
                    return this.props.connectDragSource(
                        this.props.connectDragPreview(
                            <div className="ReactDnDElement">
                                <div className="wrapper">
                                    <div className="green"></div>
                                    <div className="blue"></div>
                                    <div className="yellow"></div>
                                    <div className="magenta"></div>
                                </div>
                            </div>
                        )
                    );
                }
            });

            ReactDnDElement = ReactDnD.DragSource("dndElement", {
                beginDrag: function(props) {
                    return {};
                }
            }, function(connect, monitor) {
                return {
                    connectDragSource: connect.dragSource(),
                    connectDragPreview: connect.dragPreview()
                };
            })(ReactDnDElement);


            /* RUN IT! */

            React.render(React.createElement(ReactDnDApp), document.body);
        </script>
    </body>
</html>
bug wontfix

Most helpful comment

Well i hope the browser guys fix this issue soon..

Not likely. HTML5 drag and drop is standardized on top of IE5 behavior, and has been broken and inconsistent for ages. As web gets more complex, I suspect it will only get worse, not better. Custom mouse and touch handling is a better solution and I look forward to such React DnD backends being published.

All 5 comments

Looks like there is an issue with the native html5 drag and drop functionality as well. If i try the same with the native html5 drag and drop methods, it works correctly for firefox and safari, but not for chrome and opera.

With react-dnd this problem does not exist in safari, but for chrome, opera AND FIREFOX it does.

I have opened an issue for chromium: https://code.google.com/p/chromium/issues/detail?id=525604

Any intentions to fix this bug?

Nasty stuff. I just spent two hours on this and I have no idea how to make it work.

This seems like one of those terrible browser inconsistencies. The same x and y values passed to dataTransfer.setDragImage are interpreted by browsers differently when any child of the element you interacted with is positioned with a negative offset to its parent. Notice how changing the transform to a positive one magically makes it work.

The reason you don't see this problem on some vanilla HTML5 drag and drop examples in Firefox is likely because they don't specify x and y, and default values turn out to “just work”. However we _need_ to specify them for the dragged item appearing in the right spot in most cases.

I doubt there's any feasible workaround here. When any of the deeply nested children of the drag source node are out of its bounds to the left or the top edge, setDragImage(node, x, y) stops working correctly in some browsers. Again, even if we tried to fix it for some browsers, we'd surely break this in other browsers, so I don't think we can reliably fix this.

I'm closing, but feel free to look for workarounds! For now, I'd suggest avoiding negative transforms inside drag sources. Note that you can move connectDragSource _inside_ the transformed wrapper:

.ReactDnDElement {
    background: red;
    overflow: hidden;
    width: 200px;
    height: 100px;
}

.ReactDnDElement .transform {
    transform: translate3d(-275px, 0, 0);
    width: 100%;
    height: 100%;
}

.ReactDnDElement .wrapper {
    display: flex;
    align-items: center;
    width: 600px;
    height: 100%;
}

.ReactDnDElement .wrapper div {
    width: 150px;
    height: 50%;
}

.ReactDnDElement .wrapper div.green { background: green; }
.ReactDnDElement .wrapper div.blue { background: blue; }
.ReactDnDElement .wrapper div.yellow { background: yellow; }
.ReactDnDElement .wrapper div.magenta { background: magenta; }
var ReactDnDElement = React.createClass({
    render: function() {
      return (
        <div className="ReactDnDElement">
          <div className="transform">
            {this.props.connectDragSource(
              <div className="wrapper">
                <div className="green"></div>
                <div className="blue"></div>
                <div className="yellow"></div>
                <div className="magenta"></div>
              </div>
            )}
          </div>
        </div>
      );
    }
});

However this way only the child part will be used as the drag image—not sure if you want this.

screen shot 2015-09-17 at 17 48 31

Unfortunately this is the best I can suggest. If this is a big issue for you, please consider using an alternative React DnD backend instead of HTML5: for example, a mouse backend like in #70, which wouldn't have this problem because it wouldn't rely on setDragImage. Another option is to keep using the HTML5 backend, but use a custom drag layer which doesn't rely on setDragImage.

Hey! Thanks for having a look at this. Yes nasty stuff indeed! Well i hope the browser guys fix this issue soon..

Well i hope the browser guys fix this issue soon..

Not likely. HTML5 drag and drop is standardized on top of IE5 behavior, and has been broken and inconsistent for ages. As web gets more complex, I suspect it will only get worse, not better. Custom mouse and touch handling is a better solution and I look forward to such React DnD backends being published.

Was this page helpful?
0 / 5 - 0 ratings