I'm trying to make a website that use drag and drop functionality to build a website. But I'm facing the problem that the element is not being append before another element like the code I have shown. please help me so it can drop the elements before and after a specific element not just at the end of all the elements.
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
#div1,
#div2 {
float: left;
width: 500px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
<h2>Drag and Drop</h2>
<p>Drag the image back and forth between the two div elements.</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
<button draggable="true" ondragstart="drag(event)" id="drag1">button1</button>
<button draggable="true" ondragstart="drag(event)" id="drag2">button2</button>
<button draggable="true" ondragstart="drag(event)" id="drag3">button3</button>
<button draggable="true" ondragstart="drag(event)" id="drag4">button4</button>
</div>
<div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)">
</div>
I'm using javascript.
CodePudding user response:
You should use .prepend()
method for inserting element before the other element.
Element.prepend()
method inserts a set of Node objects or string objects before the first child of the Element. String objects are inserted as equivalent Text nodes.
Here's a Working Code :
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.prepend(document.getElementById(data));
}
#div1,
#div2 {
float: left;
width: 500px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
<h2>Drag and Drop</h2>
<p>Drag the image back and forth between the two div elements.</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
<button draggable="true" ondragstart="drag(event)" id="drag1">button1</button>
<button draggable="true" ondragstart="drag(event)" id="drag2">button2</button>
<button draggable="true" ondragstart="drag(event)" id="drag3">button3</button>
<button draggable="true" ondragstart="drag(event)" id="drag4">button4</button>
</div>
<div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)">
</div>
CodePudding user response:
That's actually quite tricky to achieve, you need to implement your own logic for inserting the element to the correct position in the list.
Here is an example where you can move items between divs
and reorder items in the same div
(Note that I only implemented the logic for drag/drop items horizontally in a single line by using left offset, if you have multiple lines items list, you need to implement your own)
function allowDrop(ev) {
ev.preventDefault()
}
function drag(ev) {
ev.dataTransfer.setData('text', ev.target.id)
}
function drop(ev) {
ev.preventDefault()
var data = ev.dataTransfer.getData('text')
// ev.currentTarget is the div that the elements are dropped
const currentDragElementOffset = ev.clientX
const items = ev.currentTarget.children
//empty list, just append and return
if (items.length === 0) {
ev.currentTarget.appendChild(document.getElementById(data))
return
}
// non-empty list
for (let i = 0; i < items.length; i ) {
const item = items[i]
const { left, width } = item.getBoundingClientRect()
// find the offset left from the center of the item
const currentElementLeftOffset = left width / 2
if (currentDragElementOffset < currentElementLeftOffset) {
ev.currentTarget.insertBefore(document.getElementById(data), item)
return
} else if (currentDragElementOffset > currentElementLeftOffset) {
ev.currentTarget.appendChild(document.getElementById(data))
}
}
}
#div1,
#div2 {
float: left;
width: 500px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
<!DOCTYPE html>
<html>
<body>
<h2>Drag and Drop</h2>
<p>Drag the image back and forth between the two div elements.</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
<button draggable="true" ondragstart="drag(event)" id="drag1">
button1
</button>
<button draggable="true" ondragstart="drag(event)" id="drag2">
button2
</button>
<button draggable="true" ondragstart="drag(event)" id="drag3">
button3
</button>
<button draggable="true" ondragstart="drag(event)" id="drag4">
button4
</button>
</div>
<div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
</body>
</html>
CodePudding user response:
Simply use the CSS property order
.
- The drop zones must have
display: flex
ordisplay: inline-flex
(see.zone
in Example - CSS) - Each item that is
draggable
should havestyle="order:
number"
(see Example - JS)
Also, to avoid an item appending to another item, use event.currentTarget
instead of event.target
in the drop()
function (see drop()
in Example - JS). In the example, the repetitive parts in HTML have been streamlined (see Figure I):
Figure I
Original Post | Example |
---|---|
Attributes on each element in HTML | Attributes assigned to each element by .forEach() |
Inline events on each element in HTML | Onevent properties bound to each element by .forEach() |
Example
Details are commented
/*
Collect all .btn into a NodeList
Iterate through NodeList with .forEach()
Bind dragstart event to current <button>
Add inline style "order" with the value of current index
Add "draggable" attribute with the value of true
*/
document.querySelectorAll('.btn').forEach((b, i) => {
b.ondragstart = drag;
b.style.order = i;
b.setAttribute('draggable', true);
});
/*
Collect all .zone into a NodeList
Iterate through NodeList with .forEach()
Bind drop event to current <div>
Bind dragover event to current <div>
*/
document.querySelectorAll('.zone').forEach(z => {
z.ondrop = drop;
z.ondragover = allowDrop;
});
function allowDrop(e) {
e.preventDefault();
}
function drag(e) {
e.dataTransfer.setData("text", e.target.id);
}
function drop(e) {
e.preventDefault();
let data = e.dataTransfer.getData("text");
/*
Change e.target to e.currentTarget to prevent any <button>
appending onto another <button>. e.currentTarget will always
point to .zone
*/
e.currentTarget.append(document.getElementById(data));
}
.zone {
display: flex;
align-items: center;
width: 500px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
.btn {
margin: 0 10px;
cursor: move;
}
<h2>Drag and Drop</h2>
<p>Drag the buttons back and forth between the two div elements.</p>
<div >
<button id='b1' class='btn'>button1</button>
<button id='b2' class='btn'>button2</button>
<button id='b3' class='btn'>button3</button>
<button id='b4' class='btn'>button4</button>
</div>
<div ></div>