0

The summary of this question is: if we're using Event Delegation does it always have to be bound to an element that exists in the DOM on page load? Can we bind to a selector returned in an ajax response?

I'm using jQuery 3.2.1 and have been reading about Event Delegation: http://learn.jquery.com/events/event-delegation/

Using their example markup:

<ul id="list">
    <li><a href="http://domain1.com">Item #1</a></li>
    <li><a href="/local/path/1">Item #2</a></li>
    <li><a href="/local/path/2">Item #3</a></li>
    <li><a href="http://domain4.com">Item #4</a></li>
</ul>

I understand the point they're making about how if we added a 5th item to #list with js, the following would only work for Items 1 - 4 because the 5th item didn't exist when .on was called and therefore doesn't get the event handler:

$( "#list a" ).on( "click", function( event ) {
});

So the solution given is use a delegated event handler like this:

$( "#list" ).on( "click", "a", function( event ) {
});

In both of these cases they have targeted #list because that's there on page load.

In my application, I am making an ajax request which would return the equivalent of the entire block of markup given at the start (i.e. everything including #list) and appending it to a Bootstrap 3.3.7 modal window with an ID, #notifierModal inside the default area for content which has a class of .modal-body:

$.ajax({
    url: $(this).attr('href'),
    method: 'get'
}).done(function(response) {
    $('.modal-body').html(response);
    $('#notifierModal').modal('show');
}); 

I have this present on page load:

$( "#list" ).on( "click", "a", function( event ) {
    console.log('something inside #list was clicked');
});

This does not work - presumably because #list is not there on page load since it's appended to .modal-body after the ajax request completes.

This does work - presumably because .modal-body is present on page load:

$( ".modal-body" ).on( "click", "a", function( event ) {
    console.log('something inside .modal-body was clicked');
});

So my question is two-fold:

  1. If we're using Event Delegation does it always have to be bound to an element that exists in the DOM on page load? Is it possible to target a selector that's returned in an ajax response?

  2. The reason this is a problem in my application is because I have other anchor tags inside .modal-body which I don't want to be handled by the click event given in my js. It seems impossible to target anchors inside #list in this way? How could I write the js such that it only targets anchors inside #list?

PS - I've read Event binding on dynamically created elements? but this seems to address binding on an element that existed on page load.

halfer
  • 18,701
  • 13
  • 79
  • 158
Andy
  • 4,326
  • 4
  • 36
  • 92

1 Answers1

1

Yes, when you use event delegation to add a handler to a container, that container must exist first in order to add the handler.

The solution here is to provide a more precise selector string when using event delegation on your .modal-body - rather than selecting any a descendant, use the selector string #list a instead to ensure that elements that trigger the listener must descend from #list:

$( ".modal-body" ).on( "click", "#list a", function( event ) {
    event.preventDefault();
    console.log('something in the list was clicked');
});

function append() {
  $( ".modal-body" ).append(`
    <ul id="list">
      <li><a href="http://domain1.com">Item #1</a></li>
      <li><a href="/local/path/1">Item #2</a></li>
      <li><a href="/local/path/2">Item #3</a></li>
      <li><a href="http://domain4.com">Item #4</a></li>
    </ul>
  `);
}

setTimeout(append, 1500);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="modal-body"><a href="foo">something that shouldn't trigger listener</a>
</div>
CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
  • Thanks for the clarification, I've got it working now. I assume you're using `setTimeout(append, 1500);` to simulate waiting for an ajax response cycle and there's no requirement to use a delay on the ajax request? – Andy Aug 20 '18 at 09:42
  • 1
    Right, that was just an example to show that the `#list` is being added asynchronously. – CertainPerformance Aug 20 '18 at 09:44