1

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?

Dzyuv001
  • 318
  • 1
  • 6
  • 18

1 Answers1

2

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>
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Thank you so much, I thought I was making my code cleaner by not using the string based selector argument. Also, thank you for pointing out that `$(this).prev('ol')` it would have taken me a lifetime to find that one out. – Dzyuv001 Jan 21 '19 at 15:56
  • 1
    Glad to help. For reference, in most situations caching selectors is a good thing, but not when working with delegated events. – Rory McCrossan Jan 21 '19 at 15:59