Search code examples
javascriptjqueryuielement

Event binding with Static Ancestors leads to every click event being triggered


I Created a small test application that should allow the user to create:

  • create list elements
  • remove list elements
  • create lists
  • remove lists

I learned that dynamically added elements don't get an event bound to them (based on this: Event binding on dynamically created elements? ) so I tried to attach the event on a single static ancestor. Unfortunately, this leads to every single click event being triggered when just one button was pressed.

<head>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <title>Test Page</title>
</head>
<body>
    <div id="container">
        <div class="listContainer">
            <ol>
                <li>First list <button class="btnDeleteListItem">Delete</button></li>
                <li>First list <button class="btnDeleteListItem">Delete</button></li>
                <li>First list <button class="btnDeleteListItem">Delete</button></li>
            </ol>
            <button class="btnAddListElement">+Add List Item</button>
            <button class="btnDeleteList">Delete List</button>
        </div>
        <button class="btnAddList">+Add List</button>
    </div>
</body>
<script>
var btnDeleteListItem = $(".btnDeleteListItem");
var btnAddListElement = $(".btnAddListElement");
var btnDeleteList = $(".btnDeleteList");
var btnAddList = $(".btnAddList");
var container = $("#container");

container.on("click",btnDeleteListItem, function (e) {
    $(this).parent().remove();
    console.log("list item deleted");
});

container.on("click",btnAddListElement, function (e) {
    var html = '<li> Just added <button class="btnDeleteListItem">Delete</button></li>';
    $("ol").append(html);
    console.log("list item added");
});

container.on("click",btnDeleteList, function (e) {
    $(this).parent().remove();
    console.log("list deleted");
});

btnAddList.on("click", function(e){
    var html = '<div class="listContainer"><ol><li>Other list <button class="btnDeleteListItem">Delete</button></li><li>Other list <button class="btnDeleteListItem">Delete</button></li><li>Other list <button class="btnDeleteListItem">Delete</button></li></ol><button class="btnAddListElement">+Add List Item</button><button class="btnDeleteList">Delete List</button></div>';
    $(html).insertBefore($(this));
    console.log("list added");
});
</script>

</html>`

Is there a way to segregate the click events?


Solution

  • The problem is because, although you're using delegated event handlers, you're only selecting the elements on load of the DOM. To fix this pass the selector argument to on() as a string. That way the selector will be matched as the click events bubble through the #container, not just for specific element instances.

    Also note that you need to make the append() call only on the ol which is relative to the .btnAddListElement button which was clicked, otherwise you end up adding a new li to every list. To do this, change $('ol') to $(this).prev('ol').

    var $container = $("#container");
    
    $container.on("click", ".btnDeleteListItem", function(e) {
      $(this).parent().remove();
      console.log("list item deleted");
    });
    
    $container.on("click", '.btnAddListElement', function(e) {
      var html = '<li> Just added <button class="btnDeleteListItem">Delete</button></li>';
      $(this).prev("ol").append(html);
      console.log("list item added");
    });
    
    $container.on("click", '.btnDeleteList', function(e) {
      $(this).parent().remove();
      console.log("list deleted");
    });
    
    $container.on('click', '.btnAddList', function(e) {
      var html = '<div class="listContainer"><ol><li>Other list <button class="btnDeleteListItem">Delete</button></li><li>Other list <button class="btnDeleteListItem">Delete</button></li><li>Other list <button class="btnDeleteListItem">Delete</button></li></ol><button class="btnAddListElement">+Add List Item</button><button class="btnDeleteList">Delete List</button></div>';
      $(html).insertBefore($(this));
      console.log("list added");
    });
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    
    <div id="container">
      <div class="listContainer">
        <ol>
          <li>First list <button class="btnDeleteListItem">Delete</button></li>
          <li>First list <button class="btnDeleteListItem">Delete</button></li>
          <li>First list <button class="btnDeleteListItem">Delete</button></li>
        </ol>
        <button class="btnAddListElement">+Add List Item</button>
        <button class="btnDeleteList">Delete List</button>
      </div>
      <button class="btnAddList">+Add List</button>
    </div>