user128511
user128511

Reputation:

Prevent a click event on parent when children are being dragged

I see a couple of similar questions. All of them say "call event.stopPropagtion()". I'm calling event.stopPropagation() but I'm still getting click events on parents. What am I doing wrong?

let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;

const px = v => `${v}px`;

function dragStart(e) {
  e.preventDefault();
  e.stopPropagation();
  dragTarget = this;
  const rect = this.getBoundingClientRect();
  dragMouseStartX = e.pageX;
  dragMouseStartY = e.pageY;
  dragTargetStartX = (window.scrollX + rect.left) | 0; 
  dragTargetStartY = (window.scrollY + rect.top) | 0; 

  window.addEventListener('mousemove', dragMove, {passive: false});
  window.addEventListener('mouseup', dragStop, {passive: false});
}

function dragMove(e) {
  if (dragTarget) {
    e.preventDefault();
    e.stopPropagation();
    const x = dragTargetStartX + (e.pageX - dragMouseStartX);
    const y = dragTargetStartY + (e.pageY - dragMouseStartY);
    dragTarget.style.left = px(x);
    dragTarget.style.top = px(y);
  }
}

function dragStop(e) {
  e.preventDefault();
  e.stopPropagation();
  dragTarget = undefined;
  window.removeEventListener('mousemove', dragMove);
  window.removeEventListener('mouseup', dragStop);
}

document.querySelector('.drag').addEventListener('mousedown', dragStart);

document.body.addEventListener('click', () => {
  console.log('body clicked', new Date());
});
body { height: 100vh; }
.drag { 
  background: red;
  color: white;
  padding: 1em;
  position: absolute;
  user-select: none;
  cursor: pointer;
}
<div class="drag">drag and release me</div>

Upvotes: 1

Views: 539

Answers (1)

dloeda
dloeda

Reputation: 1548

The problem is you're listening the mousedown event. clickevent and mousedownevent are different types of events, so to fix your code must add this:

document.querySelector('.drag').addEventListener('click', e => e.stopPropagation());

Doing this you're going to intercept the event in the children and you can handle it as you want.

let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;

const px = v => `${v}px`;

function dragStart(e) {
  e.preventDefault();
  e.stopPropagation();
  dragTarget = this;
  const rect = this.getBoundingClientRect();
  dragMouseStartX = e.pageX;
  dragMouseStartY = e.pageY;
  dragTargetStartX = (window.scrollX + rect.left) | 0; 
  dragTargetStartY = (window.scrollY + rect.top) | 0; 

  window.addEventListener('mousemove', dragMove, {passive: false});
  window.addEventListener('mouseup', dragStop, {passive: false});
}

function dragMove(e) {
  if (dragTarget) {
    e.preventDefault();
    e.stopPropagation();
    const x = dragTargetStartX + (e.pageX - dragMouseStartX);
    const y = dragTargetStartY + (e.pageY - dragMouseStartY);
    dragTarget.style.left = px(x);
    dragTarget.style.top = px(y);
  }
}

function dragStop(e) {
  e.preventDefault();
  e.stopPropagation();
  dragTarget = undefined;
  window.removeEventListener('mousemove', dragMove);
  window.removeEventListener('mouseup', dragStop);
}

document.querySelector('.drag').addEventListener('mousedown', dragStart);
document.querySelector('.drag').addEventListener('click', e => e.stopPropagation());

document.body.addEventListener('click', () => {
  console.log('body clicked', new Date());
});
body { height: 100vh; }
.drag { 
  background: red;
  color: white;
  padding: 1em;
  position: absolute;
  user-select: none;
  cursor: pointer;
}
<div class="drag">drag and release me</div>

Upvotes: 4

Related Questions