Jadon Erwin
Jadon Erwin

Reputation: 661

Is there a way to drag and copy, but prevent the drop with "draggable"? (JavaScript)

Right now I have a functional drag and copy. I was wondering if there was any way to drag copy, but not drop. For example, if a user held the shift key, he/she could rapidly click and drop clones without dropping the dragged element. If search high and low through the documentation and haven't found anything.

Here is my code https://jsfiddle.net/jado66/f8b4ms36/2/ :

<!DOCTYPE html>
<html>
<head>
  <title>Drag Copy</title>
</head>
<style>

* {user-select: none; } 
.cell {width:50px;height:50px;
  }
img {width:50px;height:50px;cursor: move;}
table td #dropTable td:hover{background: #dedede;}
#dropTable {
    border-spacing: 5px;
    border: 1px solid #dedede;
}
.droppable:hover{
    transform: scale(1.05);
    transition-duration: 0.3s;
}

#dropTable tbody { height:300px; overflow-y:auto;  }

</style>

<body>
   

     <!-- Drag Table -->
    
        <table style="border-spacing: 5px;">
            <tr>
                <td id="image1">
                    <div class = "cell" ondrop="drop(event)" ondragover="allowDrop(event)">
                        <img class = "droppable newGate" src="droppable" src="https://picsum.photos/500/500?random=1"   
                      ondragstart="dragStart(event)" id="1" draggable="true"/> </div>
                </td>
                <td id="image2">
                    <div class = "cell " ondrop="drop(event)" ondragover="allowDrop(event)">
                        <img class = "droppable" src="https://picsum.photos/500/500?random=2"   
                      ondragstart="dragStart(event)" id="2" draggable="true" /> </div>
                </td>
    
            </tr>
        </table>
        <hr>
        <!-- Drop Table -->
        <table>
            <tr>
                <td>
                    <table id="dropTable" >
                    <tr >
                        <td> <div class = "cell"></div></td> 
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                    </tr>
                    <tr>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                    </tr>
                    </tr>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                        <td> <div class = "cell" ></div></td>
                    </tr>
                    </table>
                </td>
            </tr>
        </table>
    <script>
    
const cells = document.querySelectorAll(".cell:empty"); //We only want to add event listeners to empty cells
for (const cell of cells){
    cell.addEventListener('dragover',allowDrop);
    cell.addEventListener('drop',drop);
}

function dragStart(evt)
{
    evt.dataTransfer.setData("Text",evt.target.id);
}

function drop(evt)
{
    var data = evt.dataTransfer.getData("Text");
    var nodeCopy = document.getElementById(data).cloneNode(true);
    nodeCopy.id = Date.now(); /* We cannot use the same ID */
    evt.target.appendChild(nodeCopy);
    
    if (evt.shiftKey){
        console.log("Shift key pressed. Copy but keep drag");
      //Invoke drag command, or prevent drop, etc.
    }
}

function allowDrop(ob)
{
    ob.preventDefault();
}
    
    </script>
    </body>
    </html>

Upvotes: 4

Views: 1502

Answers (1)

MarcusOtter
MarcusOtter

Reputation: 1273

In short, no

To answer your question if it's possible to prevent the drop event when the user releases the mouse button: no.

From the spec:

Otherwise, if the user ended the drag-and-drop operation (e.g. by releasing the mouse button in a mouse-driven drag-and-drop interface), or if the drag event was canceled, then this will be the last iteration.

It might however be possible to instantly fire a new DragStart event as the user drops the node, but I couldn't get this to work. Couldn't find anything in the spec that said anything about that not being possible but I could be wrong.

A workaround for your particular use case

This is just a proof of concept fiddle, code doesn't look great and I'm sure it has bugs, but it does what you ask: https://jsfiddle.net/y9Lpxdea/3/. Hold your shift key and drag over the slots to clone the current object being held.

By storing the id of the element you want to clone from onDragStart in a variable (not in the DataTransfer object as DataTransfer.getData() is only accessible on the drop event) you can access the ID of the currently held object in the onDragEnter event and then duplicate the child from there. I left the current drag & drop functionality without holding the shift key intact.

let dragId = null;

function dragStart(evt)
{
    evt.dataTransfer.setData("Text",evt.target.id);
    dragId = evt.target.id;
}

function dragEnter(evt)
{
    if (evt.shiftKey && dragId){
        var nodeCopy = document.getElementById(dragId).cloneNode(true);
        nodeCopy.id = Date.now(); /* We cannot use the same ID */
        evt.target.innerHTML = "";
        evt.target.appendChild(nodeCopy);
    }
}

function drop(evt)
{
    var data = evt.dataTransfer.getData("Text");
    var nodeCopy = document.getElementById(data).cloneNode(true);
    nodeCopy.id = Date.now(); /* We cannot use the same ID */
    evt.target.appendChild(nodeCopy);
    dragId = null;
}

Upvotes: 2

Related Questions