Drag and drop cells on CSS Grid only works properly when moving a cell to the right, and does not work when moving a cell to the left

Viewed 162

I have a CSS Grid and I'm trying to implement a drag and drop (or drag and swap) feature. It's getting close to working as desired but as the title says, the dragged cell will not drop properly into a cell to the left of its original position. The feature only works when the cell being dragged is moved to the right. Can anyone see what the problem is?

var dragSrcEl = null;
function handleDragStart(e) {
  this.style.opacity = "0.4";
  dragSrcEl = this;
  e.dataTransfer.effectAllowed = "move";
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault();
  }
  e.dataTransfer.dropEffect = "move";
  return false;
}

function handleDragEnter(e) {
    this.classList.add("over");
}

function handleDragLeave(e) {
  this.classList.remove("over");
}

function handleDrop(e) {
  if (e.stopPropagation) {
    e.stopPropagation();
  }

  if (dragSrcEl != this) {
    this.replaceWith(this, dragSrcEl);
  }
  return false;
}

function handleDragEnd(e) {
  this.style.opacity = "1";
  items.forEach(function (item) {
    item.classList.remove("over");
  });
}
  let items = document.querySelectorAll(".grid-container .grid-item");
  items.forEach(function (item) {
    item.addEventListener("dragstart", handleDragStart, false);
    item.addEventListener("dragenter", handleDragEnter, false);
    item.addEventListener("dragover", handleDragOver, false);
    item.addEventListener("dragleave", handleDragLeave, false);
    item.addEventListener("drop", handleDrop, false);
    item.addEventListener("dragend", handleDragEnd, false);
  });
.grid-container {
       display: grid;
       grid-template-columns: repeat(3, 40px);
       grid-template-rows: repeat(4, 30px);
       margin: auto;
     }
        .grid-item {
          border: 1px solid #666;
          background-color: #ddd;
          cursor: move;
        }
          .grid-item.over {
            border: 3px dotted #666;
          }

          [draggable] {
            user-select: none;
          }
<!DOCTYPE html>
<head>

<body>
  <div class="grid-container" id="grid">
  <div draggable="true" class="grid-item" id="box1">1</div>
  <div draggable="true" class="grid-item" id="box2">2</div>
  <div draggable="true" class="grid-item" id="box3">3</div>
</div>

1 Answers

I have added six more grid items to your code, so in my sample code the grid is three by three.

Now, when you drag a grid item, you can clearly see that no swapping is taking place in either direction. For example, drag item 9 and drop it on 2. The new order will be:

1 2 9  3 4 5  6 7 8  and not  1 9 3  4 5 6  7 8 2  as expected if swapping took place

Your code is inserting the dragged item to a new position and shuffling the rest of the items.

Please have a look at the answer to the following question for a drag and drop swap implementation

var dragSrcEl = null;

function handleDragStart(e) {
  this.style.opacity = "0.4";
  dragSrcEl = this;
  e.dataTransfer.effectAllowed = "move";
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault();
  }
  e.dataTransfer.dropEffect = "move";
  return false;
}

function handleDragEnter(e) {
  this.classList.add("over");
}

function handleDragLeave(e) {
  this.classList.remove("over");
}

function handleDrop(e) {
  if (e.stopPropagation) {
    e.stopPropagation();
  }

  if (dragSrcEl != this) {
    this.replaceWith(this, dragSrcEl);
  }
  return false;
}

function handleDragEnd(e) {
  this.style.opacity = "1";
  items.forEach(function(item) {
    item.classList.remove("over");
  });
}
let items = document.querySelectorAll(".grid-container .grid-item");
items.forEach(function(item) {
  item.addEventListener("dragstart", handleDragStart, false);
  item.addEventListener("dragenter", handleDragEnter, false);
  item.addEventListener("dragover", handleDragOver, false);
  item.addEventListener("dragleave", handleDragLeave, false);
  item.addEventListener("drop", handleDrop, false);
  item.addEventListener("dragend", handleDragEnd, false);
});
.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 40px);
  grid-template-rows: repeat(4, 30px);
  margin: auto;
}

.grid-item {
  border: 1px solid #666;
  background-color: #ddd;
  cursor: move;
}

.grid-item.over {
  border: 3px dotted #666;
}

[draggable] {
  user-select: none;
}
<!DOCTYPE html>

<head>

  <body>
    <div class="grid-container" id="grid">
      <div draggable="true" class="grid-item" id="box1">1</div>
      <div draggable="true" class="grid-item" id="box2">2</div>
      <div draggable="true" class="grid-item" id="box3">3</div>
      <div draggable="true" class="grid-item" id="box4">4</div>
      <div draggable="true" class="grid-item" id="box5">5</div>
      <div draggable="true" class="grid-item" id="box6">6</div>
      <div draggable="true" class="grid-item" id="box7">7</div>
      <div draggable="true" class="grid-item" id="box8">8</div>
      <div draggable="true" class="grid-item" id="box9">9</div>
    </div>

Related