Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Event delegation does not work if the bound target is nested

For a comment list I use the event delegation pattern after a recommendation from Stackoverflow colleagues (mplungjan, Michel). It works well and I am very enthusiastic about this pattern. But as I already suspected, there will be problems if the bound element (button) contains two child elements (span, span).

Since I want to get the CommentID from the target in the parent element of the child element, it only works in the cases when you click exactly between the spans inside the button. Actually a case for currentTarget but that doesn’t work in this case because the tapped element is the whole comment list.

Question: What do I have to do to fix it?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

const commentList = document.querySelector('.comment-list');

commentList.addEventListener('click', (ev) => {
  console.log('1. clicked');
  const getObjectId = () => {
    return ev.target.parentNode.parentNode.getAttribute('data-comment-id');
  }
  
  if (! getObjectId()) return false;

  if (ev.target.classList.contains('delete')) {
    console.log('2. Delete action');
    console.log('3. for relatedID', getObjectId());
  }
  
  if (ev.target.classList.contains('edit')) {
    console.log('2. Edit action');
    console.log('3. for relatedID', getObjectId());
  }  
  
  if (ev.target.classList.contains('flag')) {
    console.log('2. Flag action');
    console.log('3. for relatedID', getObjectId());
  }    
  
});
.controller {
  display: flex;
  gap:20px;
}
.comment {
  margin-bottom: 20px;
  background: gray;
}

.controller button > span {
  background: orange;
}

.controller button span:first-child {
  margin-right: 10px;
}
<div class="comment-list">
  <div class="comment">
    <div class="content">lorem 1. Dont work! Nested button.</div>
    <div class="controller" data-comment-id="1">
      <div class="delete">
        <button class="delete"><span>delete</span><span>ICON</span></button>        
      </div>
      <div class="edit">
        <button class="edit"><span>edit</span><span>ICON</span></button>
      </div>
      <div class="flag">
        <button class="flag"><span>flag</span><span>ICON</span></button>          
      </div>
    </div>
  </div>
  
  <div class="comment">
    <div class="content">lorem 2. Work! </div>
    <div class="controller" data-comment-id="2">
      <div class="delete"><button class="delete">delete</button></div>
      <div class="edit"><button class="edit">edit</button></div>
      <div class="flag"><button class="flag">flag</button></div>
    </div>
  </div>
  
  <div class="comment">
    <div class="content">lorem 3. Work! </div>
    <div class="controller" data-comment-id="3">
      <div class="delete"><button class="delete">delete</button></div>
      <div class="edit"><button class="edit">edit</button></div>
      <div class="flag"><button class="flag">flag</button></div>
    </div>
  </div>  
  
</div>

>Solution :

Building on my last comment in the other question

const tgtButtonWhenSpansInsideButton = e.target.closest("button")

  1. Cache the objects
  2. the closest method will get the button itself even if no children
  3. Make sure you get the class from the containing element of what you want to call a button
const commentList = document.querySelector('.comment-list');
const getObjectId = (tgt) => tgt.closest('.controller').dataset.commentId;

commentList.addEventListener('click', (ev) => {
  const tgt = ev.target.closest("button")
  const objectId = getObjectId(tgt);
  if (!objectId) return;
  console.log(objectId,"clicked")
  if (tgt.classList.contains('delete')) {
    console.log('2. Delete action');
    console.log('3. for relatedID', objectId);
  }

  if (tgt.classList.contains('edit')) {
    console.log('2. Edit action');
    console.log('3. for relatedID', objectId);
  }

  if (tgt.classList.contains('flag')) {
    console.log('2. Flag action');
    console.log('3. for relatedID', objectId);
  }

});
.controller {
  display: flex;
  gap: 20px;
}

.comment {
  margin-bottom: 20px;
  background: gray;
}

.controller button>span {
  background: orange;
}

.controller button span:first-child {
  margin-right: 10px;
}
<div class="comment-list">
  <div class="comment">
    <div class="content">lorem 1. Dont work! Nested button.</div>
    <div class="controller" data-comment-id="1">
      <div class="delete">
        <button class="delete"><span>delete</span><span>ICON</span></button>
      </div>
      <div class="edit">
        <button class="edit"><span>edit</span><span>ICON</span></button>
      </div>
      <div class="flag">
        <button class="flag"><span>flag</span><span>ICON</span></button>
      </div>
    </div>
  </div>

  <div class="comment">
    <div class="content">lorem 2. Work! </div>
    <div class="controller" data-comment-id="2">
      <div class="delete"><button class="delete">delete</button></div>
      <div class="edit"><button class="edit">edit</button></div>
      <div class="flag"><button class="flag">flag</button></div>
    </div>
  </div>

  <div class="comment">
    <div class="content">lorem 3. Work! </div>
    <div class="controller" data-comment-id="3">
      <div class="delete"><button class="delete">delete</button></div>
      <div class="edit"><button class="edit">edit</button></div>
      <div class="flag"><button class="flag">flag</button></div>
    </div>
  </div>

</div>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading