1

I'm making a playlist for demonstration purposes. I have set up a unordered list nested with list items. These items have been made draggable by the "draggable=true" handler, which works great.

Here's where I'm stuck;

Once the item has been clicked, and ready to drag. I need the user to have a strong visual feedback on where the user is dropping the item. The standardisation doesn't give any visual feedback, is simply puts the list item between the others. I'm looking for way to either visually push the other list elements aside, or at the very least a highlighted border on the location the user is holding the dragged item.

What's the best way to go about this? I put in some code here just in case, but what I'm looking for is outside of the code I've written.

HTML:

<ul>
    <li draggable="true" ondragstart="dragStarted(event)" ondragover="draggingOver(event)" ondrop="dropped(event)" ondragenter="dragEnter(event)"> 
        Item 1
    </li>
    <li draggable="true" ondragstart="dragStarted(event)" ondragover="draggingOver(event)" ondrop="dropped(event)" ondragenter="dragEnter(event)"> 
        Item 2
    </li>
    <li draggable="true" ondragstart="dragStarted(event)" ondragover="draggingOver(event)" ondrop="dropped(event)" ondragenter="dragEnter(event)"> 
        Item 3
    </li>       
</ul>

JS

var source; 

function dragStarted(e){
    source = e.target;
    e.dataTransfer.setData("text/plain", e.target.innerHTML);
    e.dataTransfer.effectAllowed = "move";

}

function draggingOver(e){
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";

}

function dropped(e){
    e.preventDefault();
    e.stopPropagation();
    if (e.target.localName === 'li') {
        source.innerHTML = e.target.innerHTML;
        e.target.innerHTML = e.dataTransfer.getData("text/plain");
    }
}

How do I add code that will allow me to design visual feedback in the form of a highlighted border between the items.

2
  • you'd be much better off putting a complete and verifiable example rather than just a bit of code. Commented Feb 19, 2018 at 20:11
  • I've altered the code to reflect the problem more efficiently.
    – Nathan
    Commented Feb 19, 2018 at 21:51

1 Answer 1

2

Check these two examples.

First just highlights the border of the hovered item when something is dragged over. It uses dragenter and dragleave events to add and remove the over css class.

var source; 

var items = document.querySelectorAll('li');
for (item of items) {
  item.addEventListener('dragstart', dragStarted, false);
  item.addEventListener('drop', dropped, false);
  item.addEventListener('dragover', draggingOver, false);
  item.addEventListener('dragenter', dragEnter, false);
  item.addEventListener('dragleave', dragLeave, false);
}
document.getElementById("list").addEventListener('mouseleave', dropped, false);

function dragEnter(e){
  this.classList.add('over');
}

function dragLeave(e){
  this.classList.remove('over');
}

function dragStarted(e){
  source = e.target;
  e.dataTransfer.setData("text/plain", e.target.innerHTML);
  e.dataTransfer.effectAllowed = "move";
  this.classList.add("over");
  this.style.opacity = '0.4';
}

function draggingOver(e){
  e.preventDefault();
  e.dataTransfer.dropEffect = "move";
}

function dropped(e){
  e.preventDefault();
  e.stopPropagation();
  source.style.opacity = '1.0';
  if (e.target.localName === 'li') {
    source.innerHTML = e.target.innerHTML;
    e.target.innerHTML = e.dataTransfer.getData("text/plain");
  }
  this.classList.remove("over");
  source.classList.remove("over");
}
ul { list-style-type: none; }

li {
  border: 1px solid #000;
  border-radius: 3px;
  text-decoration: none;
  padding: 2px 2px 2px 10px;
  margin: 1px;
  width: 300px;
  transition: 0.2s ease-in-out;
}

li.over {
  border: 1px dashed orange;
}
<ul id="list">
  <li draggable="true">Sun Shining On The Moon</li>
  <li draggable="true">Airglow</li>
  <li draggable="true">Diffuse reflection</li>
  <li draggable="true">Starlight</li>
</ul>

The second example, I think, is closer to what you need. It replaces items on dragenter event using JavaScript insertBefore method.

var source; 

var items = document.querySelectorAll('li');
for (item of items) {
  item.addEventListener('dragstart', dragStarted, false);
  item.addEventListener('drop', dropped, false);
  item.addEventListener('dragover', draggingOver, false);
  item.addEventListener('dragenter', dragEnter, false);
}
document.getElementById("list").addEventListener('mouseleave', dropped, false);

function isBefore(a, b) {
    if (a.parentNode == b.parentNode) {
        for (var cur = a; cur; cur = cur.previousSibling) {
            if (cur === b) { 
                return true;
            }
        }
    }
    return false;
} 

function dragEnter(e) {
    if (isBefore(source, e.target)) {
        e.target.parentNode.insertBefore(source, e.target);
    }
    else {
        e.target.parentNode.insertBefore(source, e.target.nextSibling);
    }
}

function dragStarted(e){
  e.dataTransfer.setData("text/plain", e.target.innerHTML);
  e.dataTransfer.effectAllowed = "move";
  this.classList.add("over");
  this.style.opacity = '0.4';
  source = this;
}

function draggingOver(e){
  e.preventDefault();
  e.dataTransfer.dropEffect = "move";
}

function dropped(e){
  source.style.opacity = '1.0';
  source.classList.remove("over");
}
ul { list-style-type: none; }

li {
  border: 1px solid #000;
  border-radius: 3px;
  text-decoration: none;
  padding: 2px 2px 2px 10px;
  margin: 1px;
  width: 300px;
  transition: 0.2s ease-in-out;
}

li.over {
  border: 1px dashed orange;
  transform: scale(0.97);
}
<ul id="list">
  <li draggable="true">Sun Shining On The Moon</li>
  <li draggable="true">Airglow</li>
  <li draggable="true">Diffuse reflection</li>
  <li draggable="true">Starlight</li>
</ul>

Not the answer you're looking for? Browse other questions tagged or ask your own question.