A constant eventListener, or almost that

Asked

Viewed 36 times

4

I was always in doubt when to this, because I remember that I took my test with ajax, and every time I updated, I lost the registered event.

Example: A list of names

<script>
    $(document).ready(function(){
        $(".clickLi").click(function(){
            console.log($(this).data("age"));
        });
    });
</script>
<ul>
    <li class="clickLi" data-age="18">Nome 1</li>
    <li class="clickLi" data-age="6">Nome 2</li>
    <li class="clickLi" data-age="32">Nome 3</li>
</ul>

In this code, after clicking a li, the age will be written on the console.

Okay, but if you had a button that, by ajax, updated this list, you would lose the click function. How do I keep a system in the list?

Thank you very much.

1 answer

4

The solution to this is not to add the listeners in elements that will be removed/replaced (or that do not yet exist), but in an element prior to them in the hierarchy, whose existence is guaranteed at any time.

For example, your Ajax operation changes only the content of <ul>, you can put the Listener in own <ul> instead of putting in the individual listeners. In the latter case, you put in the body of the document, as it always exists and is ancestral to any other element. Like the click event "bubble" through the DOM hierarchy, the event can be captured at a higher level.

It’s called delegation of events, and jQuery offers a syntax for this that makes the operation very simple:

$('ul').on('click', '.clickLi', function(e) {
    $(e.target).data("age")
});

The second parameter of .on() is the selector of the target elements, which may or may not exist at the time when the Listener is created is added to the <ul>.

Note also that within the function I changed $(this) for $(e.target). That’s because $(this) would be the list itself, the <ul>, while $(e.target) will be the element clicked (the <li> with class clickLi).

Follows a simple reproduction:

// Deixa de funcionar se os LI forem substituídos
$('.clickme').click(function(e) {
  var $e = $(e.target);
  $e.text(Math.random());
});

// Sempre funciona
$('ul').on('click', '.clickme', function(e) {
  var $e = $(e.target);
  $e.css('color', 'blue');
});

$('.breakThePage').click(breakThePage);
$('.fixThePage').click(fixThePage);

// Reinsere listeners para cada elemento
function fixThePage() {
  $('.clickme').off();
  $('.clickme').click(function(e) {
    var $e = $(e.target);
    $e.text(Math.random());
  });
}

function breakThePage() {
  var $ul = $('ul');
  $ul.html(
    '<li class="clickme">1</li>' +
    '<li class="clickme">2</li>' +
    '<li class="clickme">3</li>' +
    '<li class="clickme">4</li>' +
    '<li class="clickme">5</li>'
  );
}

function dontBreakThePage() {
  var $li = $('li:nth-child(3)');
  $li.text('Changed o.O');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
  <li class="clickme">1</li>
  <li class="clickme">2</li>
  <li class="clickme">3</li>
  <li class="clickme">4</li>
  <li class="clickme">5</li>
</ul>

<button class="breakThePage">Eu quebro a página</button>
<button class="fixThePage">Eu conserto a página</button>

  • Thanks to @yamadapc for the functional example (I adapted what was in your edit suggestion).

  • Good idea, to use a different color instead of my many buttons. I tried to think of something like this, but it did not come to mind for some reason :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.