Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag and drop and drag again using pure javascript

I am attempting to do a simple drag and drop from one column to another. I am copying the element so that the list on the right can have multiple versions of the element on the left. Don't worry, I'm setting unique IDs before the actual append.

However I also want the user to be able to drag out of the box to delete that same object. But once the DIV is dropped into place (i.e. once it's in column2), it cannot be dragged again. The initial drag and drop works fine.

Solutions I've found deal with jQuery ui. I'm building an angularJS app and am not interesting in using full jQuery nor any additional plug-ins.

Help!

Sample code:

<div id="column1">    
    <div class="dragme" draggable="true" ondragstart="drag(event)">Item1</div>   
    <div class="dragme" draggable="true" ondragstart="drag(event)">Item1</div>
</div>

<div id="column2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>

<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");
    var newEl = $(document.getElementById(data)).clone()[0];
    newEl.id = newEl.id + (+new Date());
    ev.target.appendChild(newEl);
}
</script>

UPDATE

It seems that it's not the dragging and dropping that's causing the issue. Any dynamically added content won't drag. I tested this in the console.

FYI...The code below works just fine here, the problem ended up being that my drag/drop was within a draggable container.

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");
  var origThing = document.getElementById(data);
  var newThing = origThing.cloneNode(true);
    ev.target.appendChild(newThing);
}
.thing {
  width: 100px;
  height: 2em;
  padding: 0.5em;
  margin: 0.5em;
  background: rgba(0,0,0,0.8);
  color: white;
  font-family: sans-serif;
 }
.col {
  width: 130px;
  height: 450px;
  padding: 1em;
  border: 1px solid;
  border-radius: 5px;
  position: relative;
  float: left;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="col" id="col1">
  <div class="thing" draggable="true" ondragstart="drag(event)" id="thing1">THING 1</div>
  <div class="thing" draggable="true" ondragstart="drag(event)" id="thing2">THING 2</div>  
  <div class="thing" draggable="true" ondragstart="drag(event)" id="thing3">THING 3</div>  
  <div class="thing" draggable="true" ondragstart="drag(event)" id="thing4">THING 4</div>
</div>
<div class="col" id="col2" ondrop="drop(event)" ondragover="allowDrop(event)">

</div>
like image 653
Wayne F. Kaskie Avatar asked Feb 07 '23 07:02

Wayne F. Kaskie


2 Answers

Here's a pure JavaScript way to drag dynamically created divs around the window.

var divs=[];
var draggingIndex;
var isDown=false;
var startX,startY;

// add some divs dynamically
addDiv(50,50,100,75,'blue','batch1');
addDiv(250,50,50,38,'green','batch1');

// listen to mouse events
window.onmousedown=(function(e){handleMouseDown(e);});
window.onmousemove=(function(e){handleMouseMove(e);});
window.onmouseup=(function(e){handleMouseUp(e);});


function addDiv(x,y,w,h,bk,classname){
    var div=document.createElement('div');
    div.style.width=w+'px';
    div.style.height=h+'px';
    div.className=classname;
    div.style.position='absolute';
    div.style.left=x+'px';
    div.style.top=y+'px';
    div.style.background=bk;
    divs.push({div:div,w:w,h:h});
    document.body.appendChild(div);
}


function handleMouseDown(e){
    // tell the browser we're handling this event
    e.preventDefault();
    e.stopPropagation();
    // get mouse position
    startX=parseInt(e.clientX);
    startY=parseInt(e.clientY);
    // Is any div under the mouse?
    draggingIndex=undefined;
    for(var i=0;i<divs.length;i++){
        var d=divs[i];
        var x=parseInt(d.div.style.left);
        var y=parseInt(d.div.style.top);
        if(startX>x && startX<x+d.w && startY>y && startY<y+d.h){
            draggingIndex=i;
            isDown=true;
            break;
        }
    }
}

function handleMouseUp(e){
    // tell the browser we're handling this event
    e.preventDefault();
    e.stopPropagation();
    isDown=false;
}

function handleMouseMove(e){
    if(!isDown){return;}
    // tell the browser we're handling this event
    e.preventDefault();
    e.stopPropagation();
    // get mouse position
    mouseX=parseInt(e.clientX);
    mouseY=parseInt(e.clientY);
    // move the dragging div by the distance the mouse has moved
    var dragging=divs[draggingIndex].div;
    var dx=mouseX-startX;
    var dy=mouseY-startY;
    startX=mouseX;
    startY=mouseY;
    dragging.style.left=(parseInt(dragging.style.left)+dx)+'px';
    dragging.style.top=(parseInt(dragging.style.top)+dy)+'px';
}
body{ background-color: ivory; }
.batch1{border:1px solid red; }
like image 195
markE Avatar answered Feb 08 '23 19:02

markE


So, the issue turned out to be as simple as the question. My draggable items are custom directives that I set up with draggable="true" and using inline binding like ondrop="drop(event)". So, I didn't realize that elsewhere, there was some very helpful code that was not using delegation, aka, only binding on the initial load. This little caveat is one that affects ANY dynamically loaded DOM elements!

Original Code

$(document).ready(function() {
    $('.dragme').on('mousedown', function(e) {
        // disable swiper when user clicks on drawer
        $(this).parents('.swiper-wrapper').addClass('no-swiping');
    });
    $('.dragme').on('mouseup dragend',  function(e) {
        // re-enable when user is done
        $(this).parents('.swiper-wrapper').removeClass('no-swiping');
    });    
});

The fix

$(document).ready(function() {
    $(document).on('mousedown', '.dragme', function(e) {
        // disable swiper when user clicks on drawer
        $(this).parents('.swiper-wrapper').addClass('no-swiping');
    });
    $(document).on('mouseup dragend', '.dragme', function(e) {
        // re-enable when user is done
        $(this).parents('.swiper-wrapper').removeClass('no-swiping');
    });
});

Note that this code was implemented earlier on to allow dragging in a draggable container!

like image 24
Wayne F. Kaskie Avatar answered Feb 08 '23 20:02

Wayne F. Kaskie