HTML5 UL LI Draggable
Asked Answered
S

5

22

I have an unordered list:

  • List item 1
  • List item 2
  • List item 3
  • List item 4
  • List item 5

Implemented with this code:

<ul>
  <li>List item 1</li>
  <li>List item 2</li>
  <li>List item 3</li>
  <li>List item 4</li>
  <li>List item 5</li>
</ul>

Now, I want it to be draggable. For example, if I drag "List item 5" upward, I can place it between "List item 2" and "List item 3", and it'll become third.

I want to do this without jQuery, just plain Javascript. Also, I'd like to use native HTML5 draggable="true". Any help would be appreciated.

Seduction answered 8/9, 2012 at 16:19 Comment(0)
A
20

Attribute "draggable" only enables the element for dragging. You need to implement the DnD listener and implement the drop event to make the changes you want.

You will find the same problem you want to solve in this example: http://www.html5rocks.com/en/tutorials/dnd/basics/

In the example they implement drag-drop for columns A, B and C. User can change the order by DnD.

Alcoholism answered 8/9, 2012 at 17:56 Comment(0)
D
17

This code will solve your problem. Redone example with MDN Try it this jsfiddle.net

HTML 5

<ul>
  <li class="dropzone" id='0' draggable="true">List item 1</li>
  <li class="dropzone" id='1' draggable="true">List item 2</li>
  <li class="dropzone" id='2' draggable="true">List item 3</li>
  <li class="dropzone" id='3' draggable="true">List item 4</li>
  <li class="dropzone" id='4' draggable="true">List item 5</li>
</ul>

pure JS

let dragged;
let id;
let index;
let indexDrop;
let list;

  document.addEventListener("dragstart", ({target}) => {
      dragged = target;
      id = target.id;
      list = target.parentNode.children;
      for(let i = 0; i < list.length; i += 1) {
        if(list[i] === dragged){
          index = i;
        }
      }
  });

  document.addEventListener("dragover", (event) => {
      event.preventDefault();
  });

  document.addEventListener("drop", ({target}) => {
   if(target.className == "dropzone" && target.id !== id) {
       dragged.remove( dragged );
      for(let i = 0; i < list.length; i += 1) {
        if(list[i] === target){
          indexDrop = i;
        }
      }
      console.log(index, indexDrop);
      if(index > indexDrop) {
        target.before( dragged );
      } else {
       target.after( dragged );
      }
    }
  });
Dateless answered 30/12, 2019 at 20:21 Comment(2)
Code only answers are often times considered low quality. You should consider adding a description of why and how this code solves the problem.Houlihan
I had a slightly more involved structure within the <li> with <span> and existing class. I found that on drop event I had to do two things 1) check that the target was an actual li (and use target = target.parentNode if not) and 2) my li already had a class, so had to use target.className.includes('dropzone').Hippocrates
S
3

Just add draggable="true" to your li-elements.

<ol ondragstart="">
<li draggable="true" data-value="data1">List Item 1</li>
<li draggable="true" data-value="data2">List Item 2</li>
<li draggable="true" data-value="data3">List Item 3</li>
</ol>
Speos answered 8/9, 2012 at 16:28 Comment(2)
This only gives the appearance of a drag. It snaps back when you release the click.Springlet
@FooBar Incorrect, it is not just an appearance. Without it, the drag events wouldn't even work.Latham
S
3

If you are testing with Firefox, note that it also requires some data be sent in the drag operation:

function handleDragStart(e) {
  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', e.target.innerHTML);
}

myLi.addEventListener('dragstart', handleDragStart, false);

Otherwise, you wouldn't see the ghost image of the content being dragged...

Supine answered 24/5, 2013 at 16:32 Comment(0)
A
1
<ul id="parent">

    <li class="parent">List item 1</li>

    <li class="parent">List item 2</li>

    <li class="parent">List item 3</li>

    <li class="parent">List item 4</li>

    <li class="parent">List item 5</li>
</ul>

try this js

    var dragSrcEl = null;

    function handleDragStart(e) {
        // Target (this) element is the source node.
        this.style.opacity = '0.4';

        dragSrcEl = this;

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

    function handleDragOver(e) {
        if (e.preventDefault) {
            e.preventDefault(); // Necessary. Allows us to drop.
        }

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

        return false;
    }

    function handleDragEnter(e) {
        // this / e.target is the current hover target.
        this.classList.add('over');
    }

    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.
            dragSrcEl.innerHTML = this.innerHTML;
            this.innerHTML = e.dataTransfer.getData('text/html');
        }

        return false;
    }

    function handleDragEnd(e) {
        // this/e.target is the source node.

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

    var cols = document.querySelectorAll('#parent .parent');
    [].forEach.call(cols, function (col) {
        col.addEventListener('dragstart', handleDragStart, false);
        col.addEventListener('dragenter', handleDragEnter, false)
        col.addEventListener('dragover', handleDragOver, false);
        col.addEventListener('dragleave', handleDragLeave, false);
        col.addEventListener('drop', handleDrop, false);
        col.addEventListener('dragend', handleDragEnd, false);
    });
Amidst answered 6/1, 2015 at 10:5 Comment(2)
I've seen it as a similar example but with live code platform. If anyone needs, refer it. codepen.io/retrofuturistic/pen/tlbHECoarsegrained
@FredricCliver thank you for the example. Is it possible to add autoscroll ? It doesn't scroll when we reach the border of the screen (on safari).Avigation

© 2022 - 2024 — McMap. All rights reserved.