skip to Main Content

I have a div with a child element. I want to handle clicks on that specific parent div. Problem is, if I press mouse button while on child content and release it on parent, that still counts as click on parent. I want to handle only clicks that both started and ended on parent, while preserving capability for child to handle it’s own clicks.

document.querySelector('.clicker').addEventListener('click', () =>
    alert('background was clicked')
);

document.querySelector('.child').addEventListener('click', event => {
  alert('content was clicked');
  event.stopPropagation();
});
.clicker {
  background: #DADADA;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  text-align: center;
  gap: 20px;
  user-select: none;
}

.child {
  background: white;
  width: 100px;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
}
<div class="clicker">
  <div class="child">
    Press left mouse button here
  </div>
  then release it here
</div>

Here it is in a fiddle:

https://jsfiddle.net/xs9za2j5/11/

Is there non-hacky way to do that, without individual handling of mousedowns and mouseups?

2

Answers


  1. Try this:

    let isMouseDown = false;
    
    document.querySelector('.clicker').addEventListener('mousedown', () => {
      isMouseDown = true;
    });
    
    document.querySelector('.clicker').addEventListener('mouseup', () => {
      if (isMouseDown) {
        alert('background was clicked');
      }
      isMouseDown = false;
    });
    
    document.querySelector('.child').addEventListener('click', (event) => {
      if (!isMouseDown) {
        alert('content was clicked');
        event.stopPropagation();
      }
    });
    

    The mousedown event sets the isMouseDown flag to true, indicating that the mouse button is being pressed. The mouseup event then checks if the isMouseDown flag is still true, which means that the mouse button was both pressed and released on the parent. If so, it triggers the background click alert. The child’s click event handler checks if isMouseDown is false, indicating that the mouse button was not pressed and released on the parent, and only then triggers the content click alert. The event.stopPropagation() prevents the content click from propagating to the parent.

    It worked for me when I replicated your problem.
    Hope I helped.

    Login or Signup to reply.
  2. Use Event.target property to determine what is clicked at "mousedown" and "mouseup".

    Details are commented in example

    // Define a variable outside of functions.
    let clicked = null;
    
    // Register the "mousedown" event to parent element call mD(e).
    document.querySelector('.clicker').onmousedown = mD;
    // Register the "mouseup" event to parent element call mU(e).
    document.querySelector('.clicker').onmouseup = mU;
    
    /**
     * On "mousedown", define "clicked" as the element the user clicked 
     * down on.
     */
    function mD(e) {
      clicked = e.target;
    }
    
    /**
     * On "mouseup", if "clicked" is the same element the user released the
     * mouse button on, log it's className as being clicked.
     * If it isn't, nothing is logged.
     */
    function mU(e) {
      if (clicked === e.target) {
        console.log(e.target.className + " was clicked.");
      }
    }
    .clicker {
      background: #DADADA;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 20px;
      text-align: center;
      gap: 20px;
      user-select: none;
    }
    
    .child {
      background: white;
      width: 100px;
      height: 100px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    .as-console-wrapper {
      overflow: hidden;
    }
    
    .as-console-row {
      visibility: collapse;
    }
    
    .as-console-row:nth-last-of-type(-n+3) {
      visibility: visible;
    }
    <div class="clicker">
      <div class="child">
        Press left mouse button here
      </div>
      then release it here
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search