No onclick event for jQuery element added after initial page load

110 views Asked by At

In our rails app, we have a table where each tbody row is a nested field. I want to limit nested fields based on current field count. To do so, I am following this guide provided by the nested_form docs.

To better suit our app, I modified the code below.

// app/views/users/index.html.haml
:javascript
  $(function() { limitNestedFields('#{@tenant.max_users}'); })

// app/assets/javascripts/limit_nested_fields.js
function limitNestedFields(limitingFactor) {
  var fieldsCount = $('tr.fields').length,
      maxFieldsCount = limitingFactor,
      $addLink = $('.add_nested_fields');
  function toggleAddLink() {
    $addLink.toggle(fieldsCount <= maxFieldsCount)
  }
  $addLink.click(function() {
    console.log("ADDING FIELD");
    if(fieldsCount < limitingFactor){
      fieldsCount += 1;
    }
    toggleAddLink();
  });
  $('.remove_nested_field').click(function() {
    console.log("REMOVING FIELD");
    fieldsCount -= 1;
    toggleAddLink();
  });
  toggleAddLink();
}

When inserting console statements, I am able to see that clicking $addLink works as expected. When I reach limitingFactor number of tbody rows, $addLink button disappears. However, I only receive log statements if I click $('.remove_nested_field') if it was present on page load.

This is important, because say a tenant may only have 10 users, and currently has 6. Say they add 4 rows of nested fields (4 users) BEFORE hitting Update. The "Add User" button ($addLink) will toggle hide. If they incorrectly fill in a nested field and wish to delete one ($('.remove_nested_field')), then the "Add User" will only appear if they click one of the original 6 rows before adding.

1

There are 1 answers

6
Max Williams On BEST ANSWER

Try replacing

$('<selector>').click(function() {

with

$(document).delegate('<selector>', 'click', function() {

this should be triggered on elements that were added after this script was run initially.

Explanation: When you do .click you are adding events on to a known, specific, list of elements. If you add new elements they don't have the event, even if they match that selector.

With .delegate, you're putting an event onto the whole DOM (aka document) saying "when we get a click, see if it's on this selector and if so do this function". So because the selector-checking happens AFTER the click, it includes ones that have been added since.

The function .on has a similar pattern and is supposed to be used instead of delegate with more modern versions of jquery: http://api.jquery.com/delegate/