skip to Main Content

Lets say I have parent -> child -> sub child, and I want to add a click event listener to all three elements. I am trying to achieve this: when sub child is clicked, I want to skip the child listener but still want to execute the parent listeners. Is there any way to achieve this?

function parentClick() {
 alert('parent click');
}


function childClick() {
 alert('childClick');
}

function subChildClick() {
 alert('subChildClick');
}
.parent {
 width: 200px;
 height: 200px;
 background-color: green;
 color: white;
 padding: 20px;
}

.child {
 width: 150px;
 height: 150px;
 background-color: yellow;
 color: red;
 padding: 20px;
}

.sub-child {
 width: 100px;
 height: 100px;
 background-color: red;
 color: white;
 padding: 20px;
}
<div class="parent" onClick="parentClick()">
 parent
  <div class="child" onClick="childClick()">
     child
    <div class="sub-child" onClick="subChildClick()">sub child</div>
  </div>
</div>

thanks.

3

Answers


  1. Add flag that will be checked in childClick:

    let flag = false;
    
    function subChildClick() {
        flag = true;
    }
    
    function childClick() {
        if (!flag) {
            doSomeStuff();
        }
    
        flag = false;
    }
    
    Login or Signup to reply.
  2. One way (without requiring inelegant flags) is to check in the child click handler if the target of the click is the sub child.

    Using contains

    This is a better solution than using matches since you don’t need to find a good selector that will only apply to .sub-child.

    function parentClick() {
      alert('parent click');
    }
    
    
    function childClick(e, el) {
      // The extra check is because "A node is contained inside itself".
      // See https://developer.mozilla.org/en-US/docs/Web/API/Node/contains
      if (el.contains(e.target) && el !== e.target) {
         return;
      }
      alert('childClick');
    
    }
    
    function subChildClick() {
      alert('subChildClick');
    }
    div { padding: 20px }
    .parent { background-color: green }
    .child { background-color: yellow }
    .sub-child { background-color: red }
    <div class="parent" onClick="parentClick(event)">
      parent
      <div class="child" onClick="childClick(event, this)">
        child
        <div class="sub-child" onClick="subChildClick(event)">sub child</div>
      </div>
    </div>

    Using matches

    function parentClick() {
      alert('parent click');
    }
    
    
    function childClick(e) {
      if (!e.target.matches(".sub-child")) {
        alert('childClick');
      }
    }
    
    function subChildClick() {
      alert('subChildClick');
    }
    div { padding: 20px }
    .parent { background-color: green }
    .child { background-color: yellow }
    .sub-child { background-color: red }
    <div class="parent" onClick="parentClick(event)">
      parent
      <div class="child" onClick="childClick(event)">
        child
        <div class="sub-child" onClick="subChildClick(event)">sub child</div>
      </div>
    </div>
    Login or Signup to reply.
  3. The selectors in the matches methods in the switch-case doesn’t need to be this specific since you’re only listening on events in the parent.

    This way you only have one event listener and the code is relatively understandable

    document.querySelector(".btn.parent").addEventListener("click", ($event) => {
        if ($event.target.classList.contains("btn")) {
            switch (true) {
                case $event.target.matches(".btn.child"):
                    alert("child click");
                    break;
                case $event.target.matches(".btn.sub-child"):
                    alert("subchild click");
                    break;
            }
            alert("parent click");
        }
    });
    .parent {
     width: 200px;
     height: 200px;
     background-color: green;
     color: white;
     padding: 20px;
    }
    
    .child {
     width: 150px;
     height: 150px;
     background-color: yellow;
     color: red;
     padding: 20px;
    }
    
    .sub-child {
     width: 100px;
     height: 100px;
     background-color: red;
     color: white;
     padding: 20px;
    }
    <div class="parent btn">
        parent
        <div class="child btn">
            child
            <div class="sub-child btn">sub child</div>
        </div>
    </div>

    You could also use onClick

    const parentClick = ($event) => {
        if ($event.target.classList.contains("btn")) {
            switch (true) {
                case $event.target.matches(".btn.child"):
                    alert("child click");
                    break;
                case $event.target.matches(".btn.sub-child"):
                    alert("subchild click");
                    break;
            }
            alert("parent click");
        }
    };
    .parent {
     width: 200px;
     height: 200px;
     background-color: green;
     color: white;
     padding: 20px;
    }
    
    .child {
     width: 150px;
     height: 150px;
     background-color: yellow;
     color: red;
     padding: 20px;
    }
    
    .sub-child {
     width: 100px;
     height: 100px;
     background-color: red;
     color: white;
     padding: 20px;
    }
    <div class="parent btn" onClick="parentClick(event)">
        parent
        <div class="child btn">
            child
            <div class="sub-child btn">sub child</div>
        </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search