Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A couple of issues with drag + drop

I have something like the attached code where some green divs can be dragged and dropped between list elements.

There are 2 issues that I don't seem to be able to solve:

1) The "ghost" dragged image in the larger "Take Me" div is almost invisible. The user has very little idea of what is been dragged. Can this be changed somehow ?

2) The drop target is the one under the mouse pointer. This is quite logical but it makes it difficult for the user to understand where the green is going to be dropped. If it was dragged by the bottom then the green makes a big jump down when the mouse button is released. Is there a way to make the top of the ghost image snap to the pointer ?

In the final application there will be many green divs and the user should be able to slot them together with no overlap so a clear idea of what is being dragged and precision dropping are important.

Would I be better off not using the drag events and implement dragdrop manually the old-fashioned way (most likely using some kind of library) ?

I need support only for the latest browsers and have tried only Chrome and FF on Windows so far.

function initDragDrop(){

    initDragDrop.dragged = null;

    // Dragged
    $('div')
        .on('dragstart',
            function(event) {

                initDragDrop.dragged = this;
                initDragDrop.dragged.style.opacity = 0.5;
                event.originalEvent.dataTransfer.setData('text/plain', 'dummy'); 
            })
        .on('dragend', function( event ) {

            event.target.style.opacity = '';
        });
        
    // Drop targets
    $('li')
        .on('dragenter', function(event) {

            event.preventDefault(); 
        })
        .on('drop dragdrop', function(event){

            event.preventDefault(); 
            event.stopPropagation();

            event.target.classList.remove('dragover');
            if ( initDragDrop.dragged ) {

                initDragDrop.dragged.style.opacity = '';
                $(this).append($(initDragDrop.dragged));
                initDragDrop.dragged = null;
            }
        })
        .on('dragover', function(event) {

            event.target.classList.add('dragover');
            event.preventDefault(); 
        })
        .on('dragleave', function(event) {

            event.target.classList.remove('dragover');
        });
}

initDragDrop();
ul {
  width: 30em;
  display: block;
}
ul.small {
  margin-top: 4em;
  width: 10em;
}
li {
  height: 2em;
  border: 1px solid grey;
}
li.dragover {
  background: #ddd;
}
div {
  background-color: lightgreen;
  border: 2px solid darkgreen;
  position: relative;
  margin: 0;
  width: 90%;
  height: 5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li></li>
<li></li>
<li><div draggable="true">
Take Me
</div></li>
<li></li>
</ul>
<ul class="small">
<li></li>
<li></li>
<li><div draggable="true">
Take Me too
</div></li>
<li></li>
<li></li>
<li></li>
</ul>
like image 510
Francesco Nardone Avatar asked Nov 06 '22 22:11

Francesco Nardone


1 Answers

You can't style the dragged element directly. As soon as the drag is started, it is a bitmap copy of what the element looked like at this moment.

But, you can change its style (or classes) before the drag starts, and revert it back right after. So, we should be able to stylize it to be at the good position before the "screenshot" occurs.

Here is an attempt where the styling works but not the positionning… I'm trying to figure it out:

(function initDragDrop() {
  initDragDrop.dragged = null;

  // Dragged
  $('div')
    .on('dragstart', function(event) {

      initDragDrop.dragged = this;
      initDragDrop.dragged.classList.add('ghost');
      event.originalEvent.dataTransfer.setData('text/plain', 'dummy');

      // Set style for the element before dragging it
      var elm = event.target;
      elm.classList.add('dragged');
      elm.style.top = (event.clientY) - 5 + 'px';
      elm.style.left = (event.clientX) - 5 + 'px';
      
      // Reset style after drag has started
      setTimeout((function(elm) {
        return function() {
          elm.classList.remove('dragged');
          elm.style.removeProperty('top');
          elm.style.removeProperty('left');
        }
      })(elm), 1000);

    })
    .on('dragend', function(event) {
      // event.target.style.opacity = ''; // Not used
    });

  // Drop targets
  $('li')
    .on('dragenter', function(event) {
      event.preventDefault();
    })
    .on('drop dragdrop', function(event) {
      event.preventDefault();
      event.stopPropagation();
      event.target.classList.remove('dragover');
      if (initDragDrop.dragged) {
        initDragDrop.dragged.classList.remove('ghost');
        $(this).append($(initDragDrop.dragged));
        initDragDrop.dragged = null;
      }
    })
    .on('dragover', function(event) {
      event.target.classList.add('dragover');
      event.preventDefault();
    })
    .on('dragleave', function(event) {
      event.target.classList.remove('dragover');
    });
})();
ul {
  width: 30em;
  display: block;
}

ul.small {
  margin-top: 4em;
  width: 10em;
}

li {
  height: 2em;
  border: 1px solid grey;
}

li.dragover {
  background: #ddd;
}

div {
  background-color: lightgreen;
  border: 2px solid darkgreen;
  position: relative;
  margin: 0;
  width: 90%;
  height: 5em;
}

.ghost {
  opacity: 0.5;
}

.dragged {
  display: block;
  position: fixed;
  background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li></li>
  <li></li>
  <li>
    <div draggable="true">
      Take Me
    </div>
  </li>
  <li></li>
</ul>
<ul class="small">
  <li></li>
  <li></li>
  <li>
    <div draggable="true">
      Take Me too
    </div>
  </li>
  <li></li>
  <li></li>
  <li></li>
</ul>
like image 53
Takit Isy Avatar answered Nov 15 '22 05:11

Takit Isy