简体   繁体   中英

Drag and drop list items

I'm refering an example to implement a drag and drop system. The example i'm refering is the following.

https://codepen.io/retrofuturistic/pen/tlbHE

I have used the given example in my project successfully. The only issue is in this example i cannot move an element to the last position. How can i modify this solution to be able to move any item that is above the last element to the last position.

HTML

<ul id="columns">
  <li class="column" draggable="true"><header>A</header></li>
  <li class="column" draggable="true"><header>B</header></li>
  <li class="column" draggable="true"><header>C</header></li>
  <li class="column" draggable="true"><header>D</header></li>
  <li class="column" draggable="true"><header>E</header></li>
</ul>

CSS

[draggable] {
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  user-select: none;
  /* Required to make elements draggable in old WebKit */
  -khtml-user-drag: element;
  -webkit-user-drag: element;
}

#columns {
  list-style-type: none;
}

.column {
  width: 162px;
  padding-bottom: 5px;
  padding-top: 5px;
  text-align: center;
  cursor: move;
}
.column header {
  height: 20px;
  width: 150px;
  color: black;
  background-color: #ccc;
  padding: 5px;
  border-bottom: 1px solid #ddd;
  border-radius: 10px;
  border: 2px solid #666666;
}

.column.dragElem {
  opacity: 0.4;
}
.column.over {
  //border: 2px dashed #000;
  border-top: 2px solid blue;
}

JS

var dragSrcEl = null;

function handleDragStart(e) {
  // Target (this) element is the source node.
  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.outerHTML);

  this.classList.add('dragElem');
}
function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault(); // Necessary. Allows us to drop.
  }
  this.classList.add('over');

  e.dataTransfer.dropEffect = 'move';  // See the section on the DataTransfer object.

  return false;
}

function handleDragEnter(e) {
  // this / e.target is the current hover target.
}

function handleDragLeave(e) {
  this.classList.remove('over');  // this / e.target is previous target element.
}

function handleDrop(e) {
  // this/e.target is current target element.

  if (e.stopPropagation) {
    e.stopPropagation(); // Stops some browsers from redirecting.
  }

  // Don't do anything if dropping the same column we're dragging.
  if (dragSrcEl != this) {
    // Set the source column's HTML to the HTML of the column we dropped on.
    //alert(this.outerHTML);
    //dragSrcEl.innerHTML = this.innerHTML;
    //this.innerHTML = e.dataTransfer.getData('text/html');
    this.parentNode.removeChild(dragSrcEl);
    var dropHTML = e.dataTransfer.getData('text/html');
    this.insertAdjacentHTML('beforebegin',dropHTML);
    var dropElem = this.previousSibling;
    addDnDHandlers(dropElem);
    
  }
  this.classList.remove('over');
  return false;
}

function handleDragEnd(e) {
  // this/e.target is the source node.
  this.classList.remove('over');

  /*[].forEach.call(cols, function (col) {
    col.classList.remove('over');
  });*/
}

function addDnDHandlers(elem) {
  elem.addEventListener('dragstart', handleDragStart, false);
  elem.addEventListener('dragenter', handleDragEnter, false)
  elem.addEventListener('dragover', handleDragOver, false);
  elem.addEventListener('dragleave', handleDragLeave, false);
  elem.addEventListener('drop', handleDrop, false);
  elem.addEventListener('dragend', handleDragEnd, false);

}

var cols = document.querySelectorAll('#columns .column');
[].forEach.call(cols, addDnDHandlers);

The linked example simplistically assumes that you want to drag the list item before the target ( insertAdjacentHTML('beforebegin',…) ). What you're trying to do is to drag an element on either side.

One way to do that is to work out your mouse position relative to the target item, and then decide whether you're closer to the beginning or the end of the item. I have posted a modified version at: https://codepen.io/manngo/pen/eYdExOr

In short, here's what's involved.

First, you will need two classes to represent whether you're over the target:

.column.over-before {
    border-top: 2px solid red;
}
.column.over-after {
    border-bottom: 2px solid green;
}

I have used different colours only to make it easier to track.

In JavaScript, you will need to check whether you in the first half of the target or the second half. Since e.clientY gives the mouse's y-coordinate, and getBoundingClientRect() gives you the target's top and bottom, you can check whether the y-coordinate is less than the average of top and bottom:

function handleDragOver(e) {
    // …
    var top=this.getBoundingClientRect().top;
    var bottom=this.getBoundingClientRect().bottom;
    if(e.clientY<(top+bottom)/2) {
        this.classList.add('over-before');
        this.classList.remove('over-after');
    }
    else {
        this.classList.add('over-right');
        this.classList.remove('over-after');
    }
}

Any reference to removing the class name must now remove both class names:

function handleDragLeave(e) {
    this.classList.remove('dragover-before');
    this.classList.remove('dragover-after');
}
function handleDragEnd(e) {
    this.classList.remove('over-before');
    this.classList.remove('over-after');
}

When you drop the item, you need to test whether you dropping it over the first or second half, meaning before or after the item. For this you can use the class name:

function handleDrop(e) {
    e.stopPropagation();
    if (dragSrcEl != this) {
        this.parentNode.removeChild(dragSrcEl);
        var dropHTML = e.dataTransfer.getData('text/html');

        if(this.classList.contains('over-before')) {
            this.insertAdjacentHTML('beforebegin',e.dataTransfer.getData('text/html'));
            addDnDHandlers(this.previousElementSibling);
        }
        else if(this.classList.contains('over-after')) {
            this.insertAdjacentHTML('afterend',e.dataTransfer.getData('text/html'));
            addDnDHandlers(this.nextElementSibling);
        }
    }
    this.classList.remove('over-before');
    this.classList.remove('over-after');
}

Of course, you can streamline the code, and wrap it inside a utility function.

I have a fiddle which I am gradually cleaning up at: https://jsfiddle.net/internotes/gn6y3tfp/119/ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM