BB123 BB123 - 7 months ago 24
Javascript Question

jQuery: Having trouble finding an element using closest();

I'm working with jQuery and Ruby on Rails. I have a table that consists of 3 columns including a form element via select box that is using AJAX to submit the result. I want to change the color of a check mark by adding/removing a class from the stylesheet signifying whether or not the user has completed the form to that corresponding row. I can get ALL of the check marks to change when one row is complete but I can't pinpoint the check mark for the row that was complete.

This seems simple (and I thought it was) but it doesn't seem to be working for some reason.

show.html.erb

<div id="container">
<tr>

<td>
<span class="glyphicon glyphicon-ok incomplete"></span>
<%= work_set.reps %> x
<%= work_set.weight %>kg
</td>

<td>
<%= form_for object, :format => 'json' do |f| %>
<%= f.select :column2, options_for_select((0..object.column1).map {|i| [i,i]}, object.column2) %>
<% end %>
</td>

</tr>
</div>


application.js

$('#container').on('change', 'select', function(e) {
var currentTarget = $(e.currentTarget);
var form = currentTarget.parents('form');

$.ajax({
url: form.attr('action'),
type: 'POST',
data: form.serialize(),
dataType: 'json',
success: function(xhr, textStatus){

// NOT WORKING
$(this).closest('tr').find('span').removeClass('incomplete').addClass('complete');

}
});
});


style.css.scss

.glyphicon-ok.incomplete {
color: #777;
opacity: 0.5;
float: left;
}

.glyphicon-ok.complete {
color: green;
float: left;
}

Answer

Try changing it to:

currentTarget.closest('tr').find('span').removeClass('incomplete').addClass('complete');

Explanation:

The cause of the problem is that the value of this within your callback function refers to jqXHR object of that ajax call. But, $.ajax method allows you to set the value of this using context option.

  $.ajax({
    url: form.attr('action'),
    type: 'POST', 
    data: form.serialize(),
    dataType: 'json',

    context: e.currentTarget,      // < === HERE ===

    success: function(xhr, textStatus){    
      // Now, `this` refers to `e.currentTarget`
      $(this).closest('tr').find('span').removeClass('incomplete').addClass('complete');
    }
  });

Alternatively, you can refer to a local variable that refers to the DOM element you want. This the first option that is at the top of the answer.

PS. I found this SO answer about this very informative.