Drag and drop doesn’t work with SVG elements #395

Closed
shunxing opened this Issue Feb 27, 2016 · 5 comments

Projects

None yet

4 participants

@shunxing

Hi everyone,
I'm new in React so I haven't get used to its way of thinking.

I'm trying to create a network editor with react. I would like to drag and drop svg circles like d3js.

I cloned the repository and begin from the "Naive Drag Around" example.
I replaced the two div by two different circles and modify the source code into a ES6 format.

On firefox 38.0.5, I can drag these circles. However, after dragging the first circle, I have to CLICK first on the second circle. Then I can drag the second one. It seems like it activates the future dragging when I click on it.

On Chrome Version 48.0.2564.116 (64-bit), it was possible to drag the node sometimes (the circles weren't reacting everytime, I should click somewhere randomly on the svg before dragging) when I was in ES7 format. After encoding to ES6, the dragging doesn't work.

I've rendered first my GraphEditor where I wrapped the DragDropContext.

class GraphEditor extends Component {
 constructor(props) {
    super(props);
  }
  render() {
      return (<Container />)
   }     
}
export default DragDropContext(HTML5Backend)(GraphEditor);

Then I got my svgContainer where I Wrapped as my DropTarget.

const boxTarget = {
  drop(props, monitor, component) {
    const item = monitor.getItem();
    const delta = monitor.getDifferenceFromInitialOffset();
    const left = Math.round(item.left + delta.x);
    const top = Math.round(item.top + delta.y);
    component.updateCircle(item.id, left, top);
  }
};

class Container extends Component {
  constructor(props) {
    super(props);
    this.state = {
      boxes: {
        'a': { top: 20, left: 80, title: 'Drag me around' },
        'b': { top: 180, left: 20, title: 'Drag me too' }
      }
    };
  }

  updateCircle(id, left, top) {
    this.setState(update(this.state, {
      boxes: {
        [id]: {
          $merge: {
            left: left,
            top: top
          }
        }
      }
    }));
  }

  render() {
    const { connectDropTarget } = this.props;
    const { boxes} = this.state;
    return connectDropTarget(
      <svg style={stylesvg}>
        {Object.keys(boxes).map(key => {
          const { left, top } = boxes[key];
          return (
            <Graphic key={key}
                 id={key}
                 left={left}
                 top={top}/>
          );
        })}
      </svg>
    );
  }
}

Container.propTypes = {
    connectDropTarget: PropTypes.func.isRequired
  };
export default DropTarget(ItemTypes.CIRCLE, boxTarget, connect => ({
  connectDropTarget: connect.dropTarget()
}))(Container);

Inside this svgContainer, I got 2 elements in which I got one wrapped as a DragSource

const boxSource = {
  beginDrag(props) {
    const { id, left, top } = props;
    return { id, left, top };
  }
};

class Circle extends Component {
  render() {
    const { hideSourceOnDrag, left, top, connectDragSource, isDragging, children } = this.props;
    console.log(this.props)
    if (isDragging && hideSourceOnDrag) {
      return null;
    }
    return connectDragSource(
        <circle cx={left} cy={top} r="40" stroke="black" fill="red"> </circle>
    );
  }
}

export default DragSource(ItemTypes.CIRCLE, boxSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging()
}))(Circle)

Is it how I've written the code or the SVG elements which could not work with react-dnd?

I found some issues with svg and react facebook/react#3192

where does my random circles dragging come from ?

@gaearon
Owner
gaearon commented Feb 28, 2016

Can you please share a complete project reproducing the issue? Otherwise it’s hard to guess what went wrong.

On firefox 38.0.5, I can drag these circles. However, after dragging the first circle, I have to CLICK first on the second circle. Then I can drag the second one. It seems like it activates the future dragging when I click on it.

Are there any errors in the console?

it was possible to drag the node sometimes when I was in ES7 format. After encoding to ES6, the dragging doesn't work.

There can be no difference in behavior between using ES6 or decorators. If there is a difference it means there is a mistake in the ES6 code.

Is it how I've written the code or the SVG elements which could not work with react-dnd?

To be honest I wouldn’t be surprised if HTML5 drag and drop API (which is what https://github.com/gaearon/react-dnd-html5-backend uses under the hood) doesn’t work well with SVG elements. Try replacing them with divs and see if the issue persists. If it is solved then unfortunately this is due to broken browser support of drag and drop for SVG elements, and you can use onMouseMove manually without React DnD or write a custom react-dnd-mousemove-backend.

@gaearon gaearon added the needs info label Feb 28, 2016
@shunxing

Can you please share a complete project reproducing the issue? Otherwise it’s hard to guess what went wrong.

https://github.com/shunxing/react-dnd-svg
Here's the full project I created. Sorry for different mistakes I made in my code and unused variables because I'm trying all the logic of react (and also integrating redux after)

Are there any errors in the console?

No error... I just log in the console when I drag the circle and this log doesn't appear when I click for the first time ("to activate it")

Try replacing them with divs and see if the issue persists.

When I cloned the example from Github where you got div inside, it works perfectly. No problem with my browser.
By the way, it also works when I put a static svg circle inside a draggable div

@gaearon
Owner
gaearon commented Feb 28, 2016

Yep, apparently HTML5 drag and drop API doesn’t work on SVG elements.

So you can either use React DnD with a custom backend a la https://github.com/yahoo/react-dnd-touch-backend/ (except you’d handle mousemove instead), use some other library, or use onMouseMove directly. Or you can wrap your svg elements in divs.

@gaearon gaearon closed this Feb 28, 2016
@gaearon gaearon added wontfix and removed needs info labels Feb 28, 2016
@gaearon gaearon changed the title from Drag working weirdly to Drag and drop doesn’t work with SVG elements Feb 28, 2016
@benjamine

@gaearon d3-drag seems to be doing drag n drop with SVG, in case it works as inspiration.

@masonk
masonk commented Dec 18, 2016

The touch backend with mouse events enabled works well with svg elements. I don't know what bugginess they're warning about in the docs, but I assume that if it's a fundamental limitation of the non-native HTML5 API, then d3 will suffer from the same thing.

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