Search code examples
javascriptcssreactjsdrag-and-dropdrag

React Drag and Drop: Div Refuses Drag


I have tried to build a react app that has drag and drop list functionality without downloading any external packages. I found a tutorial that purports to do exactly that

This is my code:

class App extends React.Component {
  state = {
    items: ["3", "2", "1", "4"]
  };

  onDragStart = (e, index) => {
    console.log("drag start!!!");
    this.draggedItem = this.state.items[index];
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html", e.target.parentNode);
    e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
  };

  onDragOver = index => {
    const draggedOverItem = this.state.items[index];

    // if the item is dragged over itself, ignore
    if (this.draggedItem === draggedOverItem) {
      return;
    }

    // filter out the currently dragged item
    let items = this.state.items.filter(item => item !== this.draggedItem);

    // add the dragged item after the dragged over item
    items.splice(index, 0, this.draggedItem);

    this.setState({ items });
  };

  onDragEnd = () => {
    this.draggedIdx = null;
  };

  render() {
    return (
      <div className="App">
        <main>
          <h3>List of items</h3>
          <ul>
            {this.state.items.map((item, idx) => (
              <li key={item} onDragOver={() => this.onDragOver(idx)}>
                <div
                  className="drag"
                  draggable
                  onDragStart={e => this.onDragStart(e, idx)}
                  onDragEnd={this.onDragEnd}
                  style={{ cursor: "pointer" }}
                />
                <span className="content" style={{ cursor: "pointer" }}>
                  {item}
                </span>
              </li>
            ))}
          </ul>
        </main>
      </div>
    );
  }
}

I don't get any bugs in console. However, the items in the list refuse to be dragged. On the offhand chance that I can drag an item, I can't get it to where I want to go and it introduces a weird gap/space: enter image description here

This is my codesandbox:https://codesandbox.io/s/angry-dewdney-xzhsi


Solution

  • Try changing the render to have the draggable div wrap the span with the text like this:

    <div
     className="drag"
     draggable
     onDragStart={e => this.onDragStart(e, idx)}
     onDragEnd={this.onDragEnd}
     style={{ cursor: "pointer" }}
    >
       <span className="content" style={{ cursor: "pointer" }}>
          {item}
       </span>
    </div>
    

    Otherwise you have to click on the empty div which would be next to impossible as you noted. This way the draggable div wraps the text so whenever you click on the text you're clicking the div.