I would like to use drag & drop to clone elements.
Most samples, including that at Mozilla, assume that the element you’re dragging has an id
, which you can then use to clone it. You then pass this id
on using the dataTransfer
object. However, this has limited scalability, since you need to have a unique id
for each element.
Is there a way to pass the dragged element on without using an id
?
var droppable = document.querySelectorAll('.droppable');
var draggable = document.querySelectorAll('.draggable');
draggable.forEach(target=>{
target.setAttribute('draggable','true');
target.addEventListener('dragstart',dragStart);
});
droppable.forEach(target=>{
target.addEventListener('dragover',dragOver);
target.addEventListener('drop',dragDrop,true);
});
function dragOver(event) {
event.preventDefault();
}
function dragStart(event) {
console.log('Drag Start');
// event.dataTransfer.setData('text/plain',event.target.id);
}
function dragDrop(event) {
console.log('Drag Drop');
// let id = event.dataTransfer.getData('text/plain');
// let element = document.querySelector(`#${id}`).cloneNode();
// event.currentTarget.appendChild(element);
}
div#from, div.droppable {
border: thin solid #666;
min-height: 12em;
}
<div id="from">
<img src="https://upload.wikimedia.org/wikipedia/commons/1/1a/Harpo_Marx_playing_the_harp_(cropped).jpeg" width="120">
</div>
<div ></div>
CodePudding user response:
The ID is only being used here to select the element later so that it can be cloned:
let element = document.querySelector(`#${id}`).cloneNode();
The ID isn't needed - any unique string that can be used to identify a particular element would work. For example, if the draggable elements are all .draggable
, a string you could use would be the index of the dragged element.
var droppable = document.querySelectorAll('.droppable');
// turn from NodeList into array so that .indexOf can be called on it
const draggable = [...document.querySelectorAll('.draggable')];
draggable.forEach(target=>{
target.setAttribute('draggable','true');
target.addEventListener('dragstart',dragStart);
});
droppable.forEach(target=>{
target.addEventListener('dragover',dragOver);
target.addEventListener('drop',dragDrop,true);
});
function dragOver(event) {
event.preventDefault();
}
function dragStart(event) {
console.log('Drag Start');
event.dataTransfer.setData('text/plain', draggable.indexOf(event.target));
}
function dragDrop(event) {
console.log('Drag Drop');
const index = event.dataTransfer.getData('text/plain');
const element = draggable[index].cloneNode();
event.currentTarget.appendChild(element);
}
div#from, div.droppable {
border: thin solid #666;
min-height: 12em;
}
<div id="from">
<img src="https://upload.wikimedia.org/wikipedia/commons/1/1a/Harpo_Marx_playing_the_harp_(cropped).jpeg" width="120">
</div>
<div ></div>
If you're in a situation where that approach doesn't work and you can't count on having a unique string you can associate with an element, another option would be to just assign the dragged element to an outer variable instead of trying to transfer the data through the event, which would be more straightforward anyway.
var droppable = document.querySelectorAll('.droppable');
const draggable = document.querySelectorAll('.draggable');
draggable.forEach(target=>{
target.setAttribute('draggable','true');
target.addEventListener('dragstart',dragStart);
});
droppable.forEach(target=>{
target.addEventListener('dragover',dragOver);
target.addEventListener('drop',dragDrop,true);
});
function dragOver(event) {
event.preventDefault();
}
let dragged;
function dragStart(event) {
console.log('Drag Start');
dragged = event.target;
}
function dragDrop(event) {
console.log('Drag Drop');
event.currentTarget.appendChild(dragged.cloneNode());
}
div#from, div.droppable {
border: thin solid #666;
min-height: 12em;
}
<div id="from">
<img src="https://upload.wikimedia.org/wikipedia/commons/1/1a/Harpo_Marx_playing_the_harp_(cropped).jpeg" width="120">
</div>
<div ></div>
CodePudding user response:
I think the dataTransfer
object is lacking somewhat in flexibility, but I have got something working with a random attribute value:
draggable.forEach(target=>{
target.setAttribute('data-id',Math.random());
// etc
});
function dragStart(event) {
event.dataTransfer.setData('text/plain',event.target.getAttribute('data-id'));
// etc
}
function dragDrop(event) {
let id = event.dataTransfer.getData('text/plain');
let element = document.querySelector(`[data-id="${id}"]`).cloneNode();
// etc
}
This feels a little bit contrived, but it is the closest I can think of to the standard approach without requiring an actual id
.