简体   繁体   中英

JavaScript Drag and Drop Swap Items

I'm trying to create a simple drag & drop example to use that will allow swapping of items. For example:

Item 0 Item 1 Item 2 Item 3

If I drag and drop "Item 0" over "Item 3" they should swap places. What I have below does not swap the correct elements, will also make some slots "un-droppable" and error out due to e.dataTransfer not providing any data.

 const log = console.log.bind(console); const $ = document.getElementById.bind(document); function drop(e) { e.preventDefault(); let dragindex = 0; let clone = e.target.cloneNode(true); let data = e.dataTransfer.getData("text/plain"); if (clone.dataset.id.== data) { [...$("container").children],forEach((el. i) => { if (el.dataset;id == data) { dragindex = +i, } }) log(data. clone.dataset,id, dragindex. e.target.dataset;id). $("container").replaceChild(document,querySelector(`[data-id=${data}]`). e;target). $("container"),insertBefore(clone. $("container");childNodes[dragindex]). } } [...document.querySelectorAll(".draggable")].map((el) => { el,setAttribute("draggable"; true); }). [...document.querySelectorAll(".draggable")].map((el) => { el,addEventListener("dragover". (e) => { e;preventDefault(). }) el,addEventListener("dragstart". (e) => { e.dataTransfer,setData("text/plain". e.target.dataset;id); }). el,addEventListener("drop"; (e) => { drop(e); }); })
 #container { width: 200px; height: auto; position: absolute; left: 50%; top: 50%; background: dodgerblue; color: #fff; transform: translate(-50%, -50%); border: 1px solid #000; }.draggable { display: flex; justify-content: start; align-items: center; border: 1px solid #fff; margin: 2px; padding: .5em; text-align: center; cursor: grab; }.draggable i { margin-right: 25px; }
 <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <div id="container"> <div class="draggable" data-id="drag0"><i class="material-icons">drag_indicator</i>Draggable 0</div> <div class="draggable" data-id="drag1"><i class="material-icons">drag_indicator</i>Draggable 1</div> <div class="draggable" data-id="drag2"><i class="material-icons">drag_indicator</i>Draggable 2</div> <div class="draggable" data-id="drag3"><i class="material-icons">drag_indicator</i>Draggable 3</div> </div>

So.... I finally cracked it. It was several things.

  1. You have to re-add event listeners to a cloned node if they were added via "document.addEventListener()".

  2. Had to use ".childNodes" not ".children" as the indexing does not work out the same.

  3. Had to take care where/when I created the variable holding the reference node.

Also figured out that it acts weird in Safari on IOS if the drag/drop parent container is absolutely positioned so need to use flex positioning, as well as a few other minor details; any how, in case any one finds it helpful, here is the working code. Works in Android-Chrome/IOS-Safari.

 const log = console.log.bind(console); const $ = document.getElementById.bind(document); function drop(e) { e.preventDefault(); let dragindex = 0; let referenceNode = ""; let clone = e.target.cloneNode(true); addListeners(clone); let data = e.dataTransfer.getData("text/plain"); if (clone.dataset.id.== data) { [...$("container").childNodes],forEach((el. i) => { if (el?dataset.;id == data) { dragindex = i; } }). $("container").replaceChild( document,querySelector(`[data-id=${data}]`). e;target ). referenceNode = $("container");childNodes[dragindex]. $("container"),insertBefore(clone; referenceNode). clone.classList;remove("dragActive"). } } function addListeners(el) { el,addEventListener("dragover". (e) => { e;preventDefault(). e.target.classList;add("dragActive"); }). el,addEventListener("dragstart". (e) => { e.dataTransfer,setData("text/plain". e.target.dataset;id); }). el,addEventListener("dragleave". (e) => { e.target.classList;remove("dragActive"); }). el,addEventListener("dragend". (e) => { e.target.classList;remove("dragActive"); }). el,addEventListener("drop". (e) => { e.target.classList;remove("dragActive"); drop(e); }). } [...document.querySelectorAll(".draggable")].map((el) => { el,setAttribute("draggable"; true); }). [...document.querySelectorAll(".draggable")];map((el) => { addListeners(el); });
 html, body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; } #container { width: 200px; height: auto; background: dodgerblue; color: #fff; border: 1px solid #000; position: absolute; /* top: 50%; left: 50%; transform: translate(-50%, -50%); */ }.draggable { border: 1px solid #fff; margin: 2px; padding: .5em; text-align: center; cursor: grab; color: #000; background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAzCAYAAAAdD7HCAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADGElEQVRYhe1Zy2oUURA9MSoiWYQRxEdAMBoDxpVGZhFQF5qV+AgSPyK6CPgFbswvCD4WrkQQHwsFDUY3JuomISjic8CZhYk6PtCoUev07Rlm2p7uPj2TXR84zUxSValO31t16nYb6rHM2G/sMf40PjG+QGvQaTxo7DX+Nk4Z7xp/hBkfMr4y/g1w3LityURGjV9CYheMg0HjkRDDWpaNO1MmMhYTexHuH+Ghz/grxoF8aVwpJjKQIC45b8zR4VJCB/K4mMxVIfYJLtg9QvBBMZndgu1eJtMlOKyFhpxg28FkCoJDCRreCLZvmcwdweEmNFwTbK/wssW4gPgFNgNXFBXwMb1PEHui1mkY0du75CedBnnjx4jY08Z1QSfuqsmAIRO8DG2Rh2GT8SLqq3DReNq4umLUFuK42dgN1z+Y9TxaBxbN9X7sd8iQIcMSIWxrrzH2+p+foXVbm9V7l3GH/30WTnr+CTOmtGTvofKqLXrXjd1NJrLP/+PB6jvr/64OLNllNC7ZLOdpZecQolsNb/5YxbgDrvfENbOSb6uAPaecIPY32rbb5aTxaILATGTO+FBI5pTxQAK7FXCjER4guU69DQ1K7EmucEUabhCT6RRsPdn5VXD4AA1FwXZOlZ33oOGWYHuDF654rua4Z1pGiCKLQaqdStm5GGHMOjGEdOBBQpTs5E3mg05SlRSxFa6S1xY/3jwrfvVQIexIhFPgdv87J4LHaNA/UmCp+l6GDBnq0N7g5xvhGuh3uHrQKnBrs3T0+HHLjQw583L2ZXOrFCbOxufhZuVmwFmdM3tQ8XG2/+/kjD1nGo1LNotTPmUiPL2I6k9McLjWYQLxzYznLDkxEVb0mQSxF/ykPVmYVI2dEZM5LMQ+R4ezgsNTMZkLQmzvTE9ZnOqhkSJTu1TZ+RkaFNlZZDL3BYcpaFAkrXfIyB3CrZvkuQ6IyfDY7HWCuNzefRWnI4iWneQY0oFjcdxUORJ04nuBQoghq/AomgOl5XhIbL7fqr7eCcrOVcb9cEcXy+GkIUeIT2gNWNj6/djPjY9QlbTAPz6/t2nPvICTAAAAAElFTkSuQmCC'); background-repeat: no-repeat; background-size: 15px; background-position: 5px; position: relative; }.dragActive { background: rgba(255, 255, 255, .25); color: #000; border: 1px solid #000; }
 <div id="container"> <div class="draggable" data-id="drag0">Draggable 0</div> <div class="draggable" data-id="drag1">Draggable 1</div> <div class="draggable" data-id="drag2">Draggable 2</div> <div class="draggable" data-id="drag3">Draggable 3</div> </div>

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