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.
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
}
// found no insertion point, that means item was dragged to the end of the list
if (i === items.length - 1 && 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>
Simply use the CSS property order.
The drop zones must have display: flex or display: inline-flex (see .zone in Example - CSS)
Each item that is draggable should have style="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 class="zone">
<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 class="zone"></div>
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>
Related
I have image that I want to drag and drop infinitely.From first box to second box when I move,image disappear from first box.Not to lose image created same image on first box again using .appendchild(img) method.I want to drag image from initial position,not from where I dropped.So I changed draggable property as false when I dropped image into box.It creates image on first box,there's no problem.But I can't drag and drop it from first box to third again because that image has no event properties.I want to give them those properties again and it should be repeated because I want to do it forever.
Here's what I tried
<!DOCTYPE HTML>
<html>
<head>
<style>
#div1, #div2,#div3 {
float: left;
width: 100px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
</style>
<script>
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));
var img = document.createElement("img");
img.src = "https://mikasasports.com/wp-content/uploads/2015/04/MVA2001.png";
var src = document.getElementById("div1");
img.style.width="88px";
img.style.height="31px";
src.appendChild(img);
drag1.draggable=false;
}
</script>
</head>
<body>
<h2>Drag and Drop</h2>
<p>Drag the image back and forth between the three div elements.</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
<img src="https://mikasasports.com/wp-content/uploads/2015/04/MVA2001.png" draggable="true" ondragstart="drag(event)" id="drag1" width="88" height="31">
</div>
<div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<div id="div3" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
</body>
</html>
The result i want from the the below code is:
1. There are three text: Drag me!, Drag me2!, Drag me3!
2. One of the text will be drag to the last empty box
3. demo2 will display the text of the drop text
But what i have done will only display the "Drag me3!"
How can I done the 1, 2 & 3 by using if...else statement?
function dragStart(ev) {
ev.dataTransfer.setData("Text", ev.target.id);
document.getElementById("demo").innerHTML = "Started to drag the p element";
}
function allowDrop(ev) {
ev.preventDefault();
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
document.getElementById("demo").innerHTML = "The p element was dropped";
var x = document.getElementById("dragtarget1").textContent;
var y = document.getElementById("dragtarget2").textContent;
var z = document.getElementById("dragtarget3").textContent;
document.getElementById("demo2").innerHTML = x;
document.getElementById("demo2").innerHTML = y;
document.getElementById("demo2").innerHTML = z;
}
.droptarget {
float: left;
width: 100px;
height: 35px;
margin: 15px;
padding: 10px;
border: 1px solid #aaaaaa;
}
<div class="droptarget" ondrop="drop(event)" ondragover="allowDrop(event)">
<p ondragstart="dragStart(event)" draggable="true" id="dragtarget1">Drag me!</p>
</div>
<div class="droptarget" ondrop="drop(event)" ondragover="allowDrop(event)">
<p ondragstart="dragStart(event)" draggable="true" id="dragtarget2">Drag me2!</p>
</div>
<div class="droptarget" ondrop="drop(event)" ondragover="allowDrop(event)">
<p ondragstart="dragStart(event)" draggable="true" id="dragtarget3">Drag me3!</p>
</div>
<div class="droptarget" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<p style="clear:both;"><strong>Note:</strong> drag events are not supported in Internet Explorer 8 and earlier versions or Safari 5.1 and earlier versions.</p>
<p id="demo"></p>
<p id="demo2"></p>
The reason why you are seeing the behavior that you listed above is because of your line of code var x = document.getElementById("dragtarget").textContent;. What this code is doing is getting the text context of the element with that specific ID (dragtarget) and setting it to the inner HTML content of the id=demo2 every time. Instead of searching for a specific element with a specific ID, you need to use a more dynamic solution that looks in the event object received by the drop() function to determine what text to set in the demo2 element.
Hi i'm working with drag and drop but i stopped at this problem.
I have 2 columns and I want to drag divs from source to target column. The problem is that these source divs are reapeated in every source container and I need that when I move one (The div #36406 for example), all others divs in adjacent source containers with the same ID disappear of source column.
edit 1:
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.className);
}
function drop(ev) {
debugger;
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
<div style="height:50px; width:50px; border: 1px solid blue; margin-left:40%" ondrop="drop(event)" ondragover="allowDrop(event)"> DROP HERE'</div> <!-- Target -->
<div class="btn btn-warning '+parcelas[j].idlancamento+'" draggable="true" style="cursor:move" ondragstart="drag(event)">
SOURCE
</div> <!-- source -->
Hi I've posted a working example below. I set each div's data-id attribute to its ID number. I used jQuery in this example (hope that's ok). When the drag event starts, the dragStart() function is called, and it searches for each div that has a matching data-id attribute and sets the opacity to 0. The dragStop() function sets the opacity back to 1. Let me know if you have any further questions :)
function dragStart(ev) {
let id = ev.target.getAttribute("data-id");
$("[data-id=" + id + "]").not(ev.target).css("opacity", 0);
}
function dragStop(ev) {
let id = ev.target.getAttribute("data-id");
$("[data-id=" + id + "]").not(ev.target).css("opacity", 1);
}
function drop(ev) {
debugger;
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
.btn {
background-color: red;
border: 1px solid black;
width: 90px;
height: 40px;
margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btn btn-warning '+parcelas[j].idlancamento+'" data-id="123" draggable="true" style="cursor:move" ondragstart="dragStart(event)" ondragend="dragStop(event)">
ID: 123
</div>
<div class="btn btn-warning '+parcelas[j].idlancamento+'" data-id="123" draggable="true" style="cursor:move" ondragstart="dragStart(event)" ondragend="dragStop(event)">
ID: 123
</div>
<div class="btn btn-warning '+parcelas[j].idlancamento+'" data-id="123" draggable="true" style="cursor:move" ondragstart="dragStart(event)" ondragend="dragStop(event)">
ID: 123
</div>
<div class="btn btn-warning '+parcelas[j].idlancamento+'" data-id="000" draggable="true" style="cursor:move" ondragstart="dragStart(event)" ondragend="dragStop(event)">
ID: 000
</div>
Somehow my draggables arn't dragging anymore into the placeholders.
I noticed it gives an error on the appendChild in the drop() function but when I alert in there I notice it doesn't even get that far.
var people = ['Sinan', 'Babette', 'Thomas', 'Yannick', 'Nick'];
$.each(people, function(key, val) {
$('#placeholders').append('<div class="dnd" data-id="' + val + '" ondrop="drop(event)" ondragover="testDiv(event, innerHTML)"></div>');
});
$('#seizeImg img').on('dragstart', function(event) {
drag(event);
});
function testDiv(ev, x) {
if (x.length > 0) {
return false;
} else {
allowDrop(ev);
return true;
}
}
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));
var complete = false;
$.each('#seizeImg img', function(key, val) {
alert(key);
});
}
.dnd {
display: inline-block;
vertical-align: top;
width: 10%;
height: 160px;
padding: 10px;
border: 2px solid #B7E6E6;
background: rgba(51, 255, 102, 0.2);
}
#seizeImg img {
width: 10%;
height: 160px;
}
div.label {
height: 25px;
text-align: center;
border: none;
background: none;
}
div.buf {
background: rgba(255, 0, 0, 0.2);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="placeholders">
</div>
<br>
<br>
<div id="seizeImg">
<img id="Babette" src="img/Babette.jpg" draggable="true" ondrop="drop(event)" width="186" height="186">
<img id="Thomas" src="img/Thomas.jpg" draggable="true" ondrop="drop(event)" width="186" height="186">
<img id="Yannick" src="img/Yannick.jpg" draggable="true" ondrop="drop(event)" width="186" height="186">
<img id="Sinan" src="img/Sinan.jpg" ondrop="drop(event)" draggable="true" ondrop="drop(event)" width="186" height="186">
<img id="Nick" src="img/Nick.jpg" ondrop="drop(event)" draggable="true" ondrop="drop(event)" width="186" height="186">
</div>
I am assuming you are using jqueryUI with the drag and drop libraries installed. If you aren't that might make things a little easier for you.
you could make it work with:
$('#seizeImg img').draggable();
and if you wanted the drop to save data all you have to do is:
$('#placeholders').droppable({
accept: $('#seizeImg img'),
drop: function(){
//enter code here to take place whenever you drop something on it
// if you want to take a value from the object you are dropping on
// the droppable object just use:
var varID = (ui.draggable.attr("value");
}
});
Its a little more concise and jqueryUI takes out a lot of the guess work.
EDIT:
Unless you didn't include the code up top, you never call the actual jqueryUI draggable function. somewhere around
$(document).ready({function(){
// you have to include
$('seizeImg img').draggable({function(){
//add some more moving parts here if you need to
});
}
just setting draggable='true' in your HTML isn't the same thing.
The problem you are facing is caused by the lines below
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
You are trying to append an element that is not of Node type. That is happening because data is an empty string thus document.getElementById("") returns null which is not allowed to be passued to appendChild.
this is a school project. Making a 2x2 grid, pictures in 3 of them and the picture should be able to be moved from "box to box". Everything else is in order, except the lower 2 divs.
The top 2 are perfectly in line, but when trying to make the second one, they either come right next to the first ones (with a br so they're a bit lower), or then on top of each other under the first 2. I know it's not a big thing that i'm missing, but i just can't seem to figure it out.
#loota {
float: left;
width: 200px;
height: 283px;
border: 2px solid #aaaaaa;
}
#loota2 {
float: left;
width: 200px;
height: 283px;
border: 2px solid #aaaaaa;
}
<div id="loota" ondrop="tiputus(event)" ondragover="siirto(event)">
<img id="kuva1" src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder&w=200&h=283" draggable="true" ondragstart="veto(event)" width="200" height="283">
</div>
<div id="loota" ondrop="tiputus(event)" ondragover="siirto(event)">
<img id="kuva2" src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder&w=200&h=283" draggable="true" ondragstart="veto(event)" width="200" height="283">
</div>
<br>
<div id="loota2" ondrop="tiputus(event)" ondragover="siirto(event)">
<img id="kuva3" src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder&w=200&h=283" draggable="true" ondragstart="veto(event)" width="200" height="283">
</div>
<div id="loota" ondrop="tiputus(event)" ondragover="siirto(event)"></div>
The 2x2grid can be created using css nth operators.
The drag and drop can be done on individual images with a class and an operator function:
(function closedScopeForDragAndDropImages() {
var dragTarget = null;
window.addEventListener("dragover", function(event) {
// prevent default to allow drop
event.preventDefault();
}, false);
window.addEventListener("drag", function(event) {
if (event.target.className == "dropable-image") {
dragTarget = event.target;
}
// prevent default to allow drop
event.preventDefault();
}, false);
window.addEventListener("drop", function(event) {
// prevent default action (open as link for some elements)
event.preventDefault();
// move dragged elem to the selected drop target
if (event.target.className == "dropable-image") {
var src = dragTarget.getAttribute("src");
dragTarget.setAttribute("src", event.target.getAttribute("src"));
event.target.setAttribute("src", src)
}
}, false);
})();
.tile {
float: left;
width: 200px;
height: 283px;
border: 2px solid #aaaaaa;
}
.tile:nth-child(2n+3) {
clear: left;
}
<div class="tile">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder1&w=200&h=283" class="dropable-image" draggable="true" />
</div>
<div class="tile">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder2&w=200&h=283" class="dropable-image" draggable="true" />
</div>
<div class="tile">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder3&w=200&h=283" class="dropable-image" draggable="true" />
</div>
<div class="tile">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=Placeholder4&w=200&h=283" class="dropable-image" draggable="true" />
</div>