0

I am using jQuery v3.6.0 on a page that should dynamically allow a user to bookmark/unbookmark an item displayed on the page.

Here is my code snippet:

HTML

<div id="37.3" class="social-info-container">
   <div id="VK7n" class="social bookmarkable">
    <i title="Bookmarks" class="fa fa-bookmark"></i>0
   </div>
</div>

Javascript

function bookmarkItem(id, elem_id){
  let [ctid, oid] = id.split('.');
    console.log(`Posting bookmark for item with content_type: ${ctid}, object_id: ${oid}`);

    $.ajax({
        type: "POST",
    headers:{'X-CSRFToken': special_csrf_token },
        url: social_bookmark_url,
        data: {
            cid: ctid,
            oid: oid,
      note: ''
        },
        dataType: "json",
        success: function(resultData) {
      console.log(`Reponse ok: ${resultData.ok}`);
      console.log(`Elem id: ${elem_id}`);
      $(`div#${elem_id}.social.bookmarkable > i.fa.fa-bookmark`).toggleClass('bookmarked');
      
      let orig_html = $(`div#${elem_id}`).html();
      let old_count = $(`div#${elem_id}`).text();
      let new_count = resultData.count; 
      let new_html = orig_html.replace(old_count,new_count)

      console.log(old_count);
      console.log(orig_html);
      console.log(new_count);

      console.log(new_html);

      $(`div#${elem_id}`).html(new_html);
        },
        error: function(xhr, status, error) {
            if (xhr.status == 403){
        window.location.href = login_url;
      }
      else {
        alert(`Something went wrong: ${xhr.statusText}`);
      }
        }
    });  
}

$().ready(function(){
    $('.social.bookmarkable .fa.fa-bookmark').on('click', function(e) {
      alert('Clicked!');
      let elem_id = $(this).closest('div.social.bookmarkable').attr('id');

      console.log(`Elem id (1): ${elem_id}`);
      let id = $(this).closest('div.social-info-container').attr('id');
      bookmarkItem(id, elem_id);
    });
});

When I click the bookmark icon, it works ONCE - after that I have to refresh the page to get it to work again. I thought using the on() method to bind to the click event would avoid this problem.

Why is the event being triggered just once - and how do I fix this?

Homunculus Reticulli
  • 65,167
  • 81
  • 216
  • 341

1 Answers1

2

You're replacing the element that the event handler is bound to. Using .on() will only work if the element you bind it to is still around. You need to also delegate the handler to an element that will always exist, and then use eg selector context to filter for the element you want.

function bookmarkItem(id, elem_id) {
    let [ctid, oid] = id.split('.');
    console.log(`Posting bookmark for item with content_type: ${ctid}, object_id: ${oid}`);
    
    // Fake response to the AJAX call
    let resultData = {
        ok: 'ok',
        count: 1
    };

    $(`div#${elem_id}.social.bookmarkable > i.fa.fa-bookmark`).toggleClass('bookmarked');

    let orig_html = $(`div#${elem_id}`).html();
    let old_count = $(`div#${elem_id}`).text();
    let new_count = resultData.count;
    let new_html = orig_html.replace(old_count,new_count)

    console.log('old_count', old_count);
    console.log('orig_html', orig_html);
    console.log('new_count', new_count);
    console.log('new_html', new_html);

    $(`div#${elem_id}`).html(new_html);
}

$().ready(function(){
    // Attach handler to an element which does not go away, and filter
    // to only match clicks on the element you want. 
    // https://api.jquery.com/jquery/#selector-context
    $('.social.bookmarkable').on('click', '.fa.fa-bookmark', function(e) {
        let elem_id = $(this).closest('div.social.bookmarkable').attr('id');
        let id = $(this).closest('div.social-info-container').attr('id');
        
        console.log('Bookmark clicked: elem_id', elem_id, '; id', id);
        
        bookmarkItem(id, elem_id);
    });
});
.bookmarked {
    background-color: black;
    color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="37.3" class="social-info-container">
   <div id="VK7n" class="social bookmarkable">
    <i title="Bookmarks" class="fa fa-bookmark">Click me</i>0
   </div>
</div>

There are many examples of this here on SO already, searching for keywords like "jquery only once", or "jquery replace handler once" turns them up. I know this well bcs I've done that search in the past :-) Eg:

Don't Panic
  • 13,965
  • 5
  • 32
  • 51