CodeCannibal
CodeCannibal

Reputation: 344

Unexpected behavior with javascript mouse events

What I wanted: a div "dragItem" should be moved horizontally. If the mouse cursor is on top of a div with the class "tag", then the position of "dragItem" will be changed.

Actually this is working. But I got a strange behavior, when I release my mouse button while the cursor is not in the same horizontal row as where it started moving: next time I click on "dragItem" I can't see the movement until I relaese the mouse button. AND: after I released the mouse button, I am still able to move "dragItem" around.

I don't understand why this happens. While trying to debug it, I found out, that - if I click somewhere on the screen before clicking on "dragItem" - things work as expected. But why?

dragItem = document.getElementById("dragItem")

dragItem.addEventListener('mousedown', dragMouseDownHandler)

mouseIsDown = false;

function dragMouseDownHandler(event) {
  if (!mouseIsDown) {
    mouseIsDown = true;
    document.addEventListener('mousemove', mouseMove)
    document.addEventListener('mouseup', mouseUp)
  }
}

function mouseMove(event) {
  var elementsUnderMouse = document.elementsFromPoint(event.clientX, event.clientY);

  elementsUnderMouse.forEach(element => {
    if (element.classList.contains("tag")) {
      vonLinks = element.offsetLeft
      dragItem.style.left = vonLinks + 'px'
    }
  })
}


function mouseUp(event) {
  mouseIsDown = false;
  document.removeEventListener('mousemove', mouseMove)
  document.removeEventListener('mouseup', mouseUp)
}
#moveArea {
  width: 600px;
  height: 600px;
  background-color: lightgray;
  display: flex;
  flex-direction: column;
}

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

.tag {
  height: 110px;
  width: 110px;
  background-color: #b3d4fc;
  border: 1px solid black;
}

#dragItem {
  height: 100px;
  width: 100px;
  background-color: lightcoral;
  position: fixed;
}
<div id="moveArea">
  <div class="row">
    <div class="tag">
      <div id="dragItem"></div>
    </div>
    <div class="tag"></div>
    <div class="tag"></div>
    <div class="tag"></div>
  </div>
  <div class="row">
    <div class="tag"></div>
    <div class="tag"></div>
    <div class="tag"></div>
    <div class="tag"></div>
  </div>
</div>

Demo of JS mousemove problem

Upvotes: 1

Views: 58

Answers (1)

mdn
mdn

Reputation: 1

Maybe you'd be better of using the more modern Drag & Drop API to implement such mechanic.
This also:

  • brings compatibility for touch-devices
  • fixes broken mouseup behavior
  • increases performance (no z-index analysis & iteration with n Frames per Second)

Maybe you'd also want to actually move the element in DOM rather than shifting it's position visually. This way the visual and logical appearance would be aligned and processable by JS.

The attached snippet shows a crude implementation using drag & drop.

const dragItem    = document.getElementById('dragItem');
const dropTargets = document.querySelectorAll('.tag');

makeMoveableTo(dragItem, ...dropTargets);

/**
 * Makes the `element` moveable (drag & drop) to the `targetElements`.
 * @param {HTMLElement}    element
 * @param {...HTMLElement} targetElements
 */
function makeMoveableTo( element, ...targetElements )
{
    element.addEventListener('dragstart', ( event ) =>
    {
        // Remember the id of the element to drag on dragstart
        event.dataTransfer.setData('text/plain', event.target.id);
    });

    targetElements.forEach(targetElement =>
    {
        targetElement.addEventListener('dragover', ( event ) =>
        {
            // Mark target as drop-zone
            event.preventDefault();
            event.dataTransfer.dropEffect = 'move';
        });
        
        targetElement.addEventListener('drop', ( event ) =>
        {
            // On drop, read the dragged element's id and call moveTo()
            const dragElementId = event.dataTransfer.getData('text/plain');
            const dragElement   = document.getElementById(dragElementId);

            moveTo(dragElement, event.target);
        });
    });
}

/**
 * Moves the `element` to the `targetElement`.
 * @param {HTMLElement} element
 * @param {HTMLElement} targetElement
 */
function moveTo( element, targetElement )
{
    element.style.left = `${targetElement.offsetLeft}px`;
    // targetElement.append(element); // Maybe move element in DOM instead
}
#moveArea{
  width: 600px;
  height: 600px;
  background-color: lightgray;
  display: flex;
  flex-direction: column;
}

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

.tag{
  height: 110px;
  width: 110px;
  background-color: #b3d4fc;
  border: 1px solid black;


}


#dragItem{
  height: 100px;
  width: 100px;
  background-color: lightcoral;
  position: fixed;
}
<div id="moveArea">
  <div class="row">
    <div class="tag">
      <div id="dragItem" draggable="true"></div>
    </div>
    <div class="tag"></div>
    <div class="tag"></div>
    <div class="tag"></div>
  </div>
  <div class="row">
    <div class="tag"></div>
    <div class="tag"></div>
    <div class="tag"></div>
    <div class="tag"></div>
  </div>
</div>

Upvotes: 0

Related Questions