Home > OS >  append the element before and after on drop it
append the element before and after on drop it

Time:07-25

I'm trying to make a website that use drag and drop functionality to build a website. But I'm facing the problem that the element is not being append before another element like the code I have shown. please help me so it can drop the elements before and after a specific element not just at the end of all the elements.

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");
  ev.target.appendChild(document.getElementById(data));
}
#div1,
#div2 {
  float: left;
  width: 500px;
  height: 35px;
  margin: 10px;
  padding: 10px;
  border: 1px solid black;
}
<h2>Drag and Drop</h2>
<p>Drag the image back and forth between the two div elements.</p>

<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
  <button draggable="true" ondragstart="drag(event)" id="drag1">button1</button>
  <button draggable="true" ondragstart="drag(event)" id="drag2">button2</button>
  <button draggable="true" ondragstart="drag(event)" id="drag3">button3</button>
  <button draggable="true" ondragstart="drag(event)" id="drag4">button4</button>


</div>

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

</div>

I'm using javascript.

CodePudding user response:

You should use .prepend() method for inserting element before the other element.

Element.prepend() method inserts a set of Node objects or string objects before the first child of the Element. String objects are inserted as equivalent Text nodes.


Here's a Working Code :

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");
  ev.target.prepend(document.getElementById(data));
}
#div1,
#div2 {
  float: left;
  width: 500px;
  height: 35px;
  margin: 10px;
  padding: 10px;
  border: 1px solid black;
}
<h2>Drag and Drop</h2>
<p>Drag the image back and forth between the two div elements.</p>

<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
  <button draggable="true" ondragstart="drag(event)" id="drag1">button1</button>
  <button draggable="true" ondragstart="drag(event)" id="drag2">button2</button>
  <button draggable="true" ondragstart="drag(event)" id="drag3">button3</button>
  <button draggable="true" ondragstart="drag(event)" id="drag4">button4</button>


</div>

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

</div>

CodePudding user response:

That's actually quite tricky to achieve, you need to implement your own logic for inserting the element to the correct position in the list.

Here is an example where you can move items between divs and reorder items in the same div

(Note that I only implemented the logic for drag/drop items horizontally in a single line by using left offset, if you have multiple lines items list, you need to implement your own)

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')
        // ev.currentTarget is the div that the elements are dropped
        const currentDragElementOffset = ev.clientX
        const items = ev.currentTarget.children
        //empty list, just append and return
        if (items.length === 0) {
          ev.currentTarget.appendChild(document.getElementById(data))
          return
        }
        // non-empty list
        for (let i = 0; i < items.length; i  ) {
          const item = items[i]
          const { left, width } = item.getBoundingClientRect()
          // find the offset left from the center of the item
          const currentElementLeftOffset = left   width / 2
          if (currentDragElementOffset < currentElementLeftOffset) {
            ev.currentTarget.insertBefore(document.getElementById(data), item)
            return
          } else if (currentDragElementOffset > currentElementLeftOffset) {
            ev.currentTarget.appendChild(document.getElementById(data))
          }
        }
      }
#div1,
      #div2 {
        float: left;
        width: 500px;
        height: 35px;
        margin: 10px;
        padding: 10px;
        border: 1px solid black;
      }
<!DOCTYPE html>
<html>

  <body>
    <h2>Drag and Drop</h2>
    <p>Drag the image back and forth between the two div elements.</p>

    <div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
      <button draggable="true" ondragstart="drag(event)" id="drag1">
        button1
      </button>
      <button draggable="true" ondragstart="drag(event)" id="drag2">
        button2
      </button>
      <button draggable="true" ondragstart="drag(event)" id="drag3">
        button3
      </button>
      <button draggable="true" ondragstart="drag(event)" id="drag4">
        button4
      </button>
    </div>

    <div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
  </body>
</html>

CodePudding user response:

Simply use the CSS property order.

  1. The drop zones must have display: flex or display: inline-flex (see .zone in Example - CSS)
  2. Each item that is draggable should have style="order: number " (see Example - JS)

Also, to avoid an item appending to another item, use event.currentTarget instead of event.target in the drop() function (see drop() in Example - JS). In the example, the repetitive parts in HTML have been streamlined (see Figure I):

Figure I

Original Post Example
Attributes on each element in HTML Attributes assigned to each element by .forEach()
Inline events on each element in HTML Onevent properties bound to each element by .forEach()

Example

Details are commented

/*
Collect all .btn into a NodeList
Iterate through NodeList with .forEach()
Bind dragstart event to current <button>
Add inline style "order" with the value of current index
Add "draggable" attribute with the value of true
*/
document.querySelectorAll('.btn').forEach((b, i) => {
  b.ondragstart = drag;
  b.style.order = i;
  b.setAttribute('draggable', true);
});

/*
Collect all .zone into a NodeList
Iterate through NodeList with .forEach()
Bind drop event to current <div>
Bind dragover event to current <div>
*/
document.querySelectorAll('.zone').forEach(z => {
  z.ondrop = drop;
  z.ondragover = allowDrop;
});

function allowDrop(e) {
  e.preventDefault();
}

function drag(e) {
  e.dataTransfer.setData("text", e.target.id);
}

function drop(e) {
  e.preventDefault();
  let data = e.dataTransfer.getData("text");
  /*
  Change e.target to e.currentTarget to prevent any <button> 
  appending onto another <button>. e.currentTarget will always 
  point to .zone
  */
  e.currentTarget.append(document.getElementById(data));
}
.zone {
  display: flex;
  align-items: center;
  width: 500px;
  height: 35px;
  margin: 10px;
  padding: 10px;
  border: 1px solid black;
}

.btn {
  margin: 0 10px;
  cursor: move;
}
<h2>Drag and Drop</h2>
<p>Drag the buttons back and forth between the two div elements.</p>

<div >
  <button id='b1' class='btn'>button1</button>
  <button id='b2' class='btn'>button2</button>
  <button id='b3' class='btn'>button3</button>
  <button id='b4' class='btn'>button4</button>
</div>

<div ></div>

  • Related