13
votes

I am doing the simple matching game. The game has multiple question. I am using drag and drop for matching. First of all, I'll drop the image element to one container, when I select another element and try to drop it into the same container, currently it's overwriting the existing element. I want to check the container, which already has the element. If it doesn't have, allow that to drop, otherwise prevent the drop.

Code:

<!DOCTYPE HTML>
<html>
<head>
</head>
<meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap -->
    <link href="manage/css/bootstrap.min.css" rel="stylesheet">
    <!-- Custom Styles -->
    <link href="css/style.css" rel="stylesheet">
    <script src="manage/js/jquery-2.1.4.min.js"></script>
    <style>
.left, .right {
    float: left;
    width: 100px;
    height: 35px;
    margin: 10px;
    border: 1px solid black;
}
</style>
</head>
<body>
<h2>Matching the following</h2>
<div class = "container-fluid"> 
    <div class="row">
        <div class = "col-md-2">
            Option A
        </div>
        <div class = "col-md-1">
                <div class="left" id="left_1">
                    <img src="manage/images/login.png" draggable="true" ondragstart="drag(event)" id="drag1" width="88" height="31">
                </div>
        </div>
        <div class = "col-md-4">
        </div>
        <div class = "col-md-1">
                <div  id="right_1" class="right" ondrop="drop(event)" ondragover="allowDrop(event)">
                </div>
        </div>
        <div class = "col-md-2">
        Option B Matching

        </div>
    </div>

    <div class="row">
        <div class = "col-md-2">
            Option B
        </div>
        <div class = "col-md-1">
                <div class="left" id="left_2">
                    <img src="manage/images/login.png" draggable="true" ondragstart="drag(event)" id="drag2" width="88" height="31">
                </div>
        </div>
        <div class = "col-md-4">

        </div>
        <div class = "col-md-1">
                <div class="right" id="right_2" ondrop="drop(event)" ondragover="allowDrop(event)">
                </div>
        </div>
        <div class = "col-md-2">
        Option A Matching
        </div>
    </div>
</div>

<script>

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

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

function drop(event) {
    event.preventDefault();
    var rightId = event.target.id;
    console.log("before"+($("#"+rightId).children().length));
    if($("#"+rightId).children().length == 0){
        console.log($("#"+rightId).children().length);
        var data = event.dataTransfer.getData("text");
        event.target.appendChild(document.getElementById(data));
    }
    console.log("after"+($("#"+rightId).children().length));
}
</script></body></html>

Screen Shot

For Reference

I want to prevent the drop event, when container already has child elements. In the same time I need to rearrange the dropped elements, when any one container empty for swapping.

Actually i want to drag the image from left container to right container. before dropping the element to right container, i want to check if container already have another image which dropped before. if there is no image in container, allow to drop the image, or else prevent dropping the image.

Awaiting suggestions. Thanks in advance!

2
Not able to understand, provide code snippet so i can understand.Kinjal Akhani
@KinjalAkhani, Check the code which i provided in question. Actually i want to drag the image from left container to right container. before dropping the element to right container, i want to check if container already have another image which dropped before. if there is no image in container, allow to drop the image, or else prevent the dropping.Keerthivasan

2 Answers

5
votes

I think that can you check collision between elements and take decision you actions

follow the example link to see if there is a collision using vanilla javascript and jquery:

Vanilla JS Div Collision Detection

How to detect div collision in my case?

https://jsfiddle.net/jeanwfsantos/bp57zgrL/

<style>
#div1 {
  width: 100px;
  height: 100px;
  border: 1px solid #aaaaaa;
  padding: 10px;
}

.element {
  width: 100px;
  height: 100px;
}
</style>
<div 
  id="div1" 
  ondrop="drop(event)" 
  ondragover="allowDrop(event)"></div>
<div
     class="element"
     id="drag1"
     style="background: blue;"
     draggable="true"
     ondragstart="drag(event)"
     ondrag="dragMove(event)"></div>
<div
     class="element"
     id="drag2"
     style="background: red;"
     draggable="true"
     ondragstart="drag(event)"
     ondrag="dragMove(event)"></div>

<script>
const elements = document.querySelectorAll('.element')
let hasCollision = false
let offset = [0, 0]

function allowDrop(ev) {
  ev.preventDefault()
}

function drag(ev) {
  ev.dataTransfer.setData('text', ev.target.id)
  offset = [
    ev.target.offsetLeft - ev.clientX,
    ev.target.offsetTop - ev.clientY
  ]
}

function drop(ev) {
  ev.preventDefault()
  const data = ev.dataTransfer.getData('text')
  if (!hasCollision) {
    ev.target.appendChild(document.getElementById(data))
  }
}

function dragMove(e) {
  hasCollision = Array.prototype.some.call(elements, d => {
    if (d.id !== e.target.id) {
      return isCollide(e, d)
    }
    return false
  })
}

function isCollide(a, b) {
  const aRect = a.target.getBoundingClientRect()
  const bRect = b.getBoundingClientRect()
  return !(
    ((a.clientY + offset[1] + aRect.height) < (bRect.top)) ||
    (a.clientY + offset[1] > (bRect.top + bRect.height)) ||
    ((a.clientX + offset[0] + aRect.width) < bRect.left) ||
    (a.clientX + offset[0] > (bRect.left + bRect.width))
  )
}
</script>

I hope this helps you.

4
votes

I think your real problem is that you've implemented allowDrop(event) as event.preventDefault() so a drop is always permitted.

Instead what you want to do is disallow a drop in a case where the target is already occupied. Try using the following implementation of allowDrop():

function allowDrop(event) {
    var t = event.target;
    // Find the drop target
    while (t !== null && !t.classList.contains("target")) {
        t = t.parentNode;
    }
    // If the target is empty allow the drop.
    if (t && t.childNodes.length == 0) {
        event.preventDefault();
    }
    return false;
}

Here's a fiddle that shows it in action. (I freely acknowledge I borrowed based on the previous answer. :)

let offset = [0, 0]

function allowDrop(ev) {
  var t = ev.target;
  while (t !== null && !t.classList.contains("target")) {
    t = t.parentNode;
  }
  if (t && t.childNodes.length > 0) {
    return false;
  }
  ev.preventDefault()
}

function drag(ev) {
  ev.dataTransfer.setData('dragID', ev.target.id)
  offset = [
    ev.target.offsetLeft - ev.clientX,
    ev.target.offsetTop - ev.clientY
  ]
}

function drop(ev) {
  ev.preventDefault()
  const data = ev.dataTransfer.getData('dragID')
  ev.target.appendChild(document.getElementById(data))
}
.target {
  width: 100px;
  height: 100px;
  border: 1px solid #aaaaaa;
  padding: 10px;
}

.element {
  width: 100px;
  height: 100px;
}
<div class="target" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<div class="element" id="drag1" style="background: blue;" draggable="true" ondragstart="drag(event)"></div>
<div class="element" id="drag2" style="background: red;" draggable="true" ondragstart="drag(event)"></div>