Search code examples
javascripthtmlcssdrag-and-dropevent-handling

Why drag and drop execute even if drop target element class list attributes doesn't contain draggable element id?


I want a draggable element to be dropped into a drop target element if and only if the drop target element in question contains the draggable element id as one of its class attributes.

So when the draggable element with id="chigaco" is dropped into drop target element whose class list attributes contain "chigaco" the code execute as it should.

When user attempt to drop said element into drop target element whose class list attributes doesn't contain "chigaco" the drop isn't performed. So far so good.

The issue is that instead the code drops the draggable element into the drop target element with class list attribute containing draggable element id. But what I'd like is for my code to do nothing.

Hope I'm making sense. If not try use the "app" and it will be self explanatory

const drags = document.querySelectorAll(".drag");
const empties = document.querySelectorAll(".empty");


for(const drag of drags) {
  drag.addEventListener("dragstart", function(e) {
    drag.classList.add("purple")
  })
  drag.addEventListener("dragend", function(e) {
    setTimeout(() => drag.classList.remove("purple"), 0)
  })

  for(const empty of empties) {
    empty.addEventListener("dragover", function(e) {
      e.preventDefault();
      console.log("dragover");
    })
    empty.addEventListener("dragenter", function(e) {
      e.preventDefault()
      console.log("dragenter")
    })
    empty.addEventListener("dragleave", function(e) {
      console.log("leave")
      console.log(empty)
    })
    empty.addEventListener("drop", function(e) {
      if(empty.classList.contains(drag.id) === false) return
      empty.append(drag)
    })
  }
}
.container {
  display: flex;

}

.empty {
  border: 1px black solid;
  height: 310px;
  width: 310px;
  margin: 10px;
  flex-direction: row;
}

.fill {
  border: 1px black solid;
  height: 100px;
  width: 100px;
  margin: 10px;
}

.drag {
  border: 1px black solid;
  height: 300px;
  width: 300px;
  margin: 10px;
  margin-left: 0;
  flex-direction: row;
}

.cointainer1 {
  margin-left: 0;
}

.parent {
  display: flex;
  flex-direction: row;
}

.purple {
  background-color: purple;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="index.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous">
    <title></title>
  </head>
  <body>
    <div class="parent">
      <div class="container1">
        <div id="chigaco" class="drag chigaco" draggable="true">bulls</div>
        <div id="losAngeles" class="drag losAngeles" draggable="true">laker</div>
        <div id="boston" class="drag boston" draggable="true">celtics</div>
        <div id="spurs" class="drag spurs" draggable="true">san Antionio</div>
        <div id="knicks" class="drag knicks" draggable="true">New York</div>
      </div>

      <div class="container2">
        <div class="empty chigaco">chigaco</div>
        <div class="empty losAngeles">losAngeles</div>
        <div class="empty boston">boston</div>
        <div class="empty spurs">spurs</div>
        <div class="empty knicks">knicks</div>
      </div>
    </div>

  </body>
  <script src="index.js" type="text/javascript">

  </script>

</html>


Solution

  • The approach was good but there's two main errors here:

    1. Nested listeners. Each drag adds a listener to all empty, these need to be two separate loops, otherwise you are multiplying listeners (drag*empty)
    2. Not storing the dragged element, as it's needed to compare id and class

    Here's how to fix it (see comments in JS code)

    const drags = document.querySelectorAll(".drag");
    const empties = document.querySelectorAll(".empty");
    
    // dragged Element
    let currentDrag = null;
    
    // Loop for drags
    for(const drag of drags) {
      drag.addEventListener("dragstart", function(e) {
        drag.classList.add("purple");
        currentDrag = e.target; // Store the dragged element
      });
    
      drag.addEventListener("dragend", function(e) {
        setTimeout(() => {
          drag.classList.remove("purple");
          currentDrag = null; // Remove the dragged element, not required but consistent in logic
        }, 10);
      });
    }
    
    // Loop for empties
    for(const empty of empties) {
      empty.addEventListener("dragover", function(e) {
        e.preventDefault();
      });
    
      empty.addEventListener("drop", function(e, i) {
        const dropElement = e.target; // Declaring for clarity
        if(!currentDrag || dropElement.classList.contains(currentDrag.id) === false) return
        empty.append(currentDrag);
      })
    }
        .container {
          display: flex;
    
        }
    
        .empty {
          border: 1px black solid;
          height: 310px;
          width: 310px;
          margin: 10px;
          flex-direction: row;
        }
    
        .fill {
          border: 1px black solid;
          height: 100px;
          width: 100px;
          margin: 10px;
        }
    
        .drag {
          border: 1px black solid;
          height: 300px;
          width: 300px;
          margin: 10px;
          margin-left: 0;
          flex-direction: row;
        }
    
        .cointainer1 {
          margin-left: 0;
        }
    
        .parent {
          display: flex;
          flex-direction: row;
        }
    
        .purple {
          background-color: purple;
        }
    <!DOCTYPE html>
    <html lang="en" dir="ltr">
      <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous">
        <title></title>
      </head>
      <body>
        <div class="parent">
          <div class="container1">
            <div id="chigaco" class="drag chigaco" draggable="true">bulls</div>
            <div id="losAngeles" class="drag losAngeles" draggable="true">laker</div>
            <div id="boston" class="drag boston" draggable="true">celtics</div>
            <div id="spurs" class="drag spurs" draggable="true">san Antionio</div>
            <div id="knicks" class="drag knicks" draggable="true">New York</div>
          </div>
    
          <div class="container2">
            <div class="empty chigaco">chigaco</div>
            <div class="empty losAngeles">losAngeles</div>
            <div class="empty boston">boston</div>
            <div class="empty spurs">spurs</div>
            <div class="empty knicks">knicks</div>
          </div>
        </div>
      </body>
    
    </html>