javascriptaddeventlistenerevent-delegation

Event delegation from target to all inner elements when HTML is dynamically appended?


I have this basic HTML that represents a button, I want to attach a click event, but this button is appended dynamically to the container through a an ajax function, so because this is not present initially in the DOM, I'm attaching the event to the container in this way

document.querySelector('#container').addEventListener('click', (e) => {
  
  var t = e.target.classList;
  if( t.contains('button-outer') ) console.log(e.target);
  
});
#container {
  width: 100%;
  height: 100%;
  position: relative;
  text-align: center;
}

.button-outer {
  padding: 15px;
  background-color: orange;
  display: inline-block;
  margin: 0 auto;
  text-align: center;
}

.button-inner{
  font-weight: bold;
  cursor: pointer;
  font-size: 75px;
}
<div id="container">
  <div class="button-outer">
    <div class="button-inner">BUTTON</div>
  </div>
</div>

This works, but obviously only when I'm clicking on on the padding part of the outer div. To fix this I have to change the if statement in a way that it will contains the inner part too, like this:

document.querySelector('#container').addEventListener('click', (e) => {

  var t = e.target.classList;
  if( t.contains('button-outer') || t.contains('button-inner')) console.log(e.target);
  
});

I think that this is a little inconvenient, because sometimes the inner part could have other classes, or could be an icon, a link, it is a little difficult to create specific statements each time, so question is:

How can I propagate the event starting from outer to all inner elements when the button is dynamically appended?


Solution

  • You should attach your event handler when the button is created, in your ajax function.

    But if you need to do it the way you are doing it, you can use closest(), it will traverse all of the target's parents until it finds your query.

    document.querySelector('#container').addEventListener('click', (e) => {
      
      var t = e.target;
      if(t.closest('.button-outer')) console.log(e.target);
      
    });
    #container {
      width: 100%;
      height: 100%;
      position: relative;
      text-align: center;
    }
    
    .button-outer {
      padding: 15px;
      background-color: orange;
      display: inline-block;
      margin: 0 auto;
      text-align: center;
    }
    
    .button-inner{
      font-weight: bold;
      cursor: pointer;
      font-size: 75px;
    }
    <div id="container">
      <div class="button-outer">
        <div class="button-inner">BUTTON</div>
      </div>
    </div>