I tried implementing drag and drop functionality in reactjs
using vanilla HTML/js
APIs. I almost completed it, but I cannot drop it between the existing div
s. I want to add the functionality of dragging and dropping in both the divs (i.e., I should be able to drag any of the div
s in the first column and drop anywhere in the second column and vice versa). So far, I am able to drag and drop only at the last index
, not in between
Here is what I have tried so far. Please include the code. I am not that strong to follow if you are suggesting something
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const App = () => {
const drop = (e) => {
e.preventDefault();
const div_id = e.dataTransfer.getData("div_id");
const block = document.getElementById(div_id);
e.target.appendChild(block);
};
const dragOver1 = (e) => {
e.preventDefault();
};
const dragStart = (e) => {
const target = e.target;
e.dataTransfer.setData("div_id", target.id);
};
const dragOver = (e) => {
e.stopPropagation();
};
return (
<div
style={{
display: "flex",
justifyContent: "space-between",
padding: "50px",
}}
>
<div
onDrop={drop}
onDragOver={dragOver1}
id="board-1"
style={{
border: "1px solid #222",
padding: 20,
}}
>
<div
id="firstfirst"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<div>
<h1>First Column First Row</h1>
</div>
</div>
<div
id="firstsecond"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<div>
<h1>First Column Second Row</h1>
</div>
</div>
{Array.from(Array(2)).map((_, index) => {
return (
<div
key={index}
id={`first${index}`}
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1>First Column Row {index}</h1>
</div>
);
})}
</div>
<div
id="board-2"
onDrop={drop}
onDragOver={dragOver1}
style={{
border: "1px solid #222",
padding: 20,
}}
>
<div
id="secondfirst"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1>Second Column First Row</h1>
</div>
<div
id="secondsecond"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1>Second Column Second Row</h1>
</div>
{Array.from(Array(2)).map((c, index) => {
return (
<div
key={index}
id={`second${index}`}
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1> Second Column Row {index} </h1>
</div>
);
})}
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
</script>
CodePudding user response:
appendChild
always adds the item to the end of the target div
. You can do a simple calculation to identify the position to drop using children's positions (child.getBoundingClientRect().bottom
) and mouse drop position (e.clientY
).
Change the drop
handler as below
const drop = e => {
e.preventDefault();
const div_id = e.dataTransfer.getData("div_id");
const block = document.getElementById(div_id);
// get the child div index where to drop
let dropIndex = Array.from(e.target.children).findIndex(
// find the first child index where
// a child bottom (vertical viewport height to the bottom of child div)
// is greater than mouse Y position
child => child.getBoundingClientRect().bottom > e.clientY
);
if (dropIndex === -1) {
// drop at the end
e.target.appendChild(block);
} else {
// drop at start or in between
e.target.insertBefore(block, e.target.children[dropIndex]);
}
};
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const App = () => {
const drop = (e) => {
e.preventDefault();
const div_id = e.dataTransfer.getData("div_id");
const block = document.getElementById(div_id);
let dropIndex = Array.from(e.target.children).findIndex(
(child) => child.getBoundingClientRect().bottom > e.clientY
);
if (dropIndex === -1) {
e.target.appendChild(block);
} else {
e.target.insertBefore(block, e.target.children[dropIndex]);
}
};
const dragOver1 = (e) => {
e.preventDefault();
};
const dragStart = (e) => {
const target = e.target;
e.dataTransfer.setData("div_id", target.id);
};
const dragOver = (e) => {
e.stopPropagation();
};
return (
<div
style={{
display: "flex",
justifyContent: "space-between",
padding: "50px",
}}
>
<div
onDrop={drop}
onDragOver={dragOver1}
id="board-1"
style={{
border: "1px solid #222",
padding: 20,
}}
>
<div
id="firstfirst"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<div>
<h1>First Column First Row</h1>
</div>
</div>
<div
id="firstsecond"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<div>
<h1>First Column Second Row</h1>
</div>
</div>
{Array.from(Array(2)).map((_, index) => {
return (
<div
key={index}
id={`first${index}`}
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1>First Column Row {index}</h1>
</div>
);
})}
</div>
<div
id="board-2"
onDrop={drop}
onDragOver={dragOver1}
style={{
border: "1px solid #222",
padding: 20,
}}
>
<div
id="secondfirst"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1>Second Column First Row</h1>
</div>
<div
id="secondsecond"
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1>Second Column Second Row</h1>
</div>
{Array.from(Array(2)).map((c, index) => {
return (
<div
key={index}
id={`second${index}`}
draggable
onDragStart={dragStart}
onDragOver={dragOver}
>
<h1> Second Column Row {index} </h1>
</div>
);
})}
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
</script>