0

Helllo, fellow StackOverflow community! For the past few weeks, I have been working on a drag-and-drop for HTML/JavaScript. Basically, I have a circle and two boxes. The circle can be dragged to anywhere on the screen. However, I need it so the circle can only be dropped within one of the two boxes. Despite several days of research, I could not figure out how to do this. Thank you in advance!

<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" 
        content="width=device-width, 
        initial-scale=1.0, 
        user-scalable=no" />
  <title>Drag/Drop/Bounce</title>
  <style>
    #item {
      width: 100px;
      height: 100px;
      background-color: rgb(245, 230, 99);
      border: 10px solid rgba(136, 136, 136, .5);
      border-radius: 50%;
      touch-action: none;
      user-select: none;
    }

    #box1 {
      width: 200px;
      height: 200px;
      background-color: red;
    }

    #box2 {
      width: 200px;
      height: 200px;
      background-color: lightblue;
    }
  </style>
</head>

<body>
<h1>Drag and Drop</h1>
<div id="box1">
  <div id="item"></div>
</div>
<br>
<div id="box2"></div>

  <script>
    var dragItem = document.querySelector("#item");
    var container = dragItem;

    //Declare Variables
    var active = false;
    var currentX;
    var currentY;
    var initialX;
    var initialY;
    var xOffset = 0;
    var yOffset = 0;

    //Add Event Listeners for Touchscreens
    container.addEventListener("touchstart", dragStart, false);
    container.addEventListener("touchend", dragEnd, false);
    container.addEventListener("touchmove", drag, false);

    //Add Event Listeners for Mice
    container.addEventListener("mousedown", dragStart, false);
    container.addEventListener("mouseup", dragEnd, false);
    container.addEventListener("mousemove", drag, false);

    function dragStart(e) { //when the drag starts
      if (e.type === "touchstart") { //if its a touchscreen
        initialX = e.touches[0].clientX - xOffset; //set initial x-cordinate to where it was before drag started
        initialY = e.touches[0].clientY - yOffset; //set initial y-cordinate to where it was before drag started
      } else { //if its not a touchscreen (mouse)
        initialX = e.clientX - xOffset; //set initial x-cordinate to where it was before drag started
        initialY = e.clientY - yOffset; //set initial y-cordinate to where it was before drag started
      }

      if (e.target === dragItem) { //if user is dragging circle
        active = true; //the drag is active
      }
    }

    function dragEnd(e) { //when the drag ends
      initialX = currentX; //set the initial x-cordinate to where it is now
      initialY = currentY; //set the initial y-cordinate to where it is now

      active = false; //the drag is no longer active
    }

    function drag(e) { //when the circle is being dragged
      if (active) { //if the drag is active
        e.preventDefault(); //the user cant do anything else but drag

        if (e.type === "touchmove") { //if its a touchscreen
          currentX = e.touches[0].clientX - initialX; //set current x-cordinate to where it is now
          currentY = e.touches[0].clientY - initialY; //set current y-cordinate to where it is now
        } else { //if its not a touchscreen (mouse)
          currentX = e.clientX - initialX; //set current x-cordinate to where it is now
          currentY = e.clientY - initialY; //set current y-cordinate to where it is now
        }

        //Im not sure what this does but it dosnt work without it
        xOffset = currentX;
        yOffset = currentY;
        setTranslate(currentX, currentY, dragItem);
      }
    }

    function setTranslate(xPos, yPos, el) { //Im not sure what this does but it dosnt work without it
      el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
    }
  </script>
</body>

</html>

I've also uploaded the code to TryIt for those who prefer: https://www.w3schools.com/code/tryit.asp?filename=GCF4V2RIT7T4

mattywelch
  • 49
  • 1
  • 9
  • Have you read through [the documentation](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API)? It's pretty straight-forward. Seems like you want to read up on the section titled "Define a drop zone". Also, please always post your code right here in your question, rather than at 3rd party locations as those links can die over time, making your question meaningless for those who come across it. – Scott Marcus Mar 01 '20 at 21:05
  • Also, W3 Schools is well-known to have incomplete, out of date, or just plain incorrect information. Get your information from the source, which is the [Mozilla Developers Network](https://developer.mozilla.org/). – Scott Marcus Mar 01 '20 at 21:06
  • W3Schools had a lot of inaccurate information in their articles in the past, but they have mostly shaped-up. Unfortunately they still suffer from a bad reputation from people like you who haven't been on W3S in a while and don't know they changed. It doesn't matter anyway because I didn't link to a W3Schools article. I linked to their code sharing platform TryIt, which has my own code. If you click the link, you will see that that it is user-made code. That user is me. It's my code, not W3Schools' code. – mattywelch Mar 01 '20 at 21:58
  • Also, it seems the documentation you linked, Mr. Marcus, is discussing drop events, such as the ones in [one of my previous questions](https://stackoverflow.com/questions/60245082/). I am using touch events and mouse events here. I don't believe I'm using any drop events. – mattywelch Mar 01 '20 at 22:30

1 Answers1

1

In this version you can move the circle only inside the red box. I think you can take that and easily implement the other box.

<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" 
    content="width=device-width, 
    initial-scale=1.0, 
    user-scalable=no" />
  <title>Drag/Drop/Bounce</title>
  <style>
#item {
  width: 100px;
  height: 100px;
  background-color: rgb(245, 230, 99);
  border: 10px solid rgba(136, 136, 136, .5);
  border-radius: 50%;
  touch-action: none;
  user-select: none;
  position: absolute;
}

#box1 {
  width: 200px;
  height: 200px;
  background-color: red;
}

#box2 {
  width: 200px;
  height: 200px;
  background-color: lightblue;
}
  </style>
</head>

<body>
<h1>Drag and Drop</h1>
<div id="item"></div>
<div id="box1">
</div>
<br>
<div id="box2"></div>

  <script>
var dragItem = document.querySelector("#item");
var box1 = document.querySelector("#box1");
var box2 = document.querySelector("#box2");
var container = dragItem;
//Declare Variables
var active = false;
var currentX;
var currentY;
var initialX;
var initialY;
var xOffset = 0;
var yOffset = 0;

//Add Event Listeners for Touchscreens
container.addEventListener("touchstart", dragStart, false);
container.addEventListener("touchend", dragEnd, false);
container.addEventListener("touchmove", drag, false);

//Add Event Listeners for Mice
container.addEventListener("mousedown", dragStart, false);
container.addEventListener("mouseup", dragEnd, false);
container.addEventListener("mousemove", drag, false);

function dragStart(e) { //when the drag starts
  if (e.type === "touchstart") { //if its a touchscreen
    initialX = e.touches[0].clientX - xOffset; //set initial x-cordinate to where it was before drag started
    initialY = e.touches[0].clientY - yOffset; //set initial y-cordinate to where it was before drag started
  } else { //if its not a touchscreen (mouse)
    initialX = e.clientX - xOffset; //set initial x-cordinate to where it was before drag started
    initialY = e.clientY - yOffset; //set initial y-cordinate to where it was before drag started
  }
  if (e.target === dragItem) { //if user is dragging circle
    active = true; //the drag is active
  }
}

function dragEnd(e) { //when the drag ends
  const box1Size = box1.getBoundingClientRect()
  const elementSize = dragItem.getBoundingClientRect()
  if (elementSize.left >= box1Size.left && elementSize.right <= box1Size.right && elementSize.top >= box1Size.top && elementSize.bottom <= box1Size.bottom) {
    initialX = currentX; //set the initial x-cordinate to where it is now
    initialY = currentY; //set the initial y-cordinate to where it is now
  } else {
    currentX = 0
    currentY = 0
 initialX = 0
    initialY = 0
    xOffset = 0;
    yOffset = 0;
   setTranslate(0, 0, dragItem);
  }
  
  active = false; //the drag is no longer active
}

function drag(e) { //when the circle is being dragged
  if (active) { //if the drag is active
    e.preventDefault(); //the user cant do anything else but drag
  
    if (e.type === "touchmove") { //if its a touchscreen
      currentX = e.touches[0].clientX - initialX; //set current x-cordinate to where it is now
      currentY = e.touches[0].clientY - initialY; //set current y-cordinate to where it is now
    } else { //if its not a touchscreen (mouse)
      currentX = e.clientX - initialX; //set current x-cordinate to where it is now
      currentY = e.clientY - initialY; //set current y-cordinate to where it is now
    }
    
    //Im not sure what this does but it dosnt work without it
    xOffset = currentX;
    yOffset = currentY;
    setTranslate(currentX, currentY, dragItem);
  }
}

function setTranslate(xPos, yPos, el) { //Im not sure what this does but it dosnt work without it
  el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
  </script>
</body>

</html>
Johnny Zabala
  • 2,158
  • 1
  • 11
  • 14
  • That's almost perfect! I can handle the other box but do you know how to make it so the circle goes back to where it was before if it wasn't dropped in a box, instead of it going back to the upper-left corner of the red box. I tried setTranslate(initialX, initialY, dragItem); but that didn't seem to work. Thanks! – mattywelch Mar 01 '20 at 23:08