3

I have interesting problem and i can't figure it out why it's happening like that. I have dataTables and data comes after selection change on a select, with jquery ajax post. And i have onclick function for multiple selection. (It must be run when click at table and it changes rows style etc.) I noticed that (with debug); when i click on row after first load onclick works one time as expected. But click after second load (selection changed) it runs 2 time and click after third load it runs 3 time i don't understand what's going on. So need some help.

Here is selection change function that loads the table;

// in doc.ready
$('#groupSelect').change(function() {
  var group = $('#groupSelect').val();

  if (!$.fn.DataTable.isDataTable('#questTable')) //this is for first load
  {
    GetQuestions(group);
  } else //this is for after first load
  {
    var table = $('#questTable').DataTable();
    table.destroy();
    table.clear().draw();
    GetQuestions(group);
  }
});

And this is GetQuestions() function that gets data;

// out of doc ready
function GetQuestions(questGroup) {
  $.ajax({
    type: 'POST',
    dataType: 'json',
    contentType: 'application/json',
    url: 'SetAudit.aspx/Questions',
    data: '{"q_group":"' + questGroup + '"}',
    success: function(result) {
      $('#questTable').DataTable({
        data: result.d,
        columns: [{
          data: 'q_id'
        }, {
          data: 'q_text'
        }]
      });


      //this click function runs multiple time at 1 click
      $('#questTable tbody').on('click', 'tr', function() {
        var table = $('#questTable').DataTable();
        var count = table.rows('.selected').count();
        $(this).toggleClass('selected');
        $('#selectedCount').text('' + table.rows('.selected').count() + '');
      });
    }
  });
}

I don't if it is ok that i created it in ajax success func but it doesn't work anywhere else. Thanks in advance.

Gangadhar Jannu
  • 4,136
  • 6
  • 29
  • 49
Flardryn
  • 457
  • 1
  • 10
  • 25

5 Answers5

4

The issue is because every time a change event occurs on #groupSelect you fire an AJAX request, and in the success handler of that AJAX request you attach another click event handler to the tr of the table. Hence they duplicate.

To fix this I'd suggest you move the tr event handler outside the success handler and only run it once on load of the DOM. Try this:

function GetQuestions(questGroup) {
    $.ajax({
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: 'SetAudit.aspx/Questions',
        data: { q_group: questGroup },
        success: function (result) {
            $('#questTable').DataTable({
                data: result.d,
                columns: [
                    { data: 'q_id' },
                    { data: 'q_text' }
                ]
            });
        }
    });
}

// do this on load *only*
$('#questTable tbody').on('click', 'tr', function () {
    var table = $('#questTable').DataTable();
    var count = table.rows('.selected').count();
    $(this).toggleClass('selected');
    $('#selectedCount').text(table.rows('.selected').count());
});
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Yes sir i didn't like using this function in ajax success at the begining. But when i create it outside (like your answer) it doesn't run. I think my real problem is that. This onclick doesn't work anywhere else. What can i do? – Flardryn Mar 08 '17 at 14:08
  • Change the event handler signature: http://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements – freedomn-m Mar 08 '17 at 14:11
  • It sounds like `#questTable tbody` may not be in the DOM when it loads. Try changing it to `$(document).on(...` – Rory McCrossan Mar 08 '17 at 14:16
  • 1
    @RoryMcCrossan Yes you were right. I solved it with $(document).on() structure. Also http://stackoverflow.com/questions/8110934/direct-vs-delegated-jquery-on this article did really usefull to get it. And you make me understand the real problem. --Sorry for late response-- Thank you for your help and time. – Flardryn Mar 08 '17 at 18:56
2

This should work

 //this click function runs multiple time at 1 click
$('#questTable tbody').off().on('click', 'tr', function() {
  var table = $('#questTable').DataTable();
  var count = table.rows('.selected').count();
  $(this).toggleClass('selected');
  $('#selectedCount').text('' + table.rows('.selected').count() + '');
});

There are multiple ways you can solve the issue.

Removing and Adding the table DOM element: It depends on the way you construct data table. If you are constructing your datatable only from JS then you can go with this approach.

// in doc.ready
$('#groupSelect').change(function() {
  var group = $('#groupSelect').val();

  if (!$.fn.DataTable.isDataTable('#questTable')) {// this is for first load
    GetQuestions(group);
  } else {//this is for after first load
    var table = $('#questTable').DataTable();
    table.destroy();
    table.clear().draw();
    // empty the table which will eventually clear all the event handlers
    $('#questTable').empty();
    GetQuestions(group);
  }
});

Using drawCallback event of datatable along with jQuery off: You can place the row highlighting function in drawCallback

//out of doc ready
function GetQuestions(questGroup) {
  $.ajax({
    type: 'POST',
    dataType: 'json',
    contentType: 'application/json',
    url: 'SetAudit.aspx/Questions',
    data: '{"q_group":"' + questGroup + '"}',
    success: function(result) {
      $('#questTable').DataTable({
        data: result.d,
        columns: [{
          data: 'q_id'
        }, {
          data: 'q_text'
        }],
        drawCallback: function(settings) {
          //this click function runs multiple time at 1 click
          $('#questTable tbody').off().on('click', 'tr', function() {
            var table = $('#questTable').DataTable();
            var count = table.rows('.selected').count();
            $(this).toggleClass('selected');
            $('#selectedCount').text('' + table.rows('.selected').count() + '');
          });
        }
      });
    }
  });
}
Gangadhar Jannu
  • 4,136
  • 6
  • 29
  • 49
  • I had been trying to use as you gived second section of your answer. I couldn't fire the onclick like that but now; i get it why and i've solved it with McCrossan's advice. Thank you for this usefull and detailed answer. – Flardryn Mar 08 '17 at 19:05
0

You're adding the binding to the click event inside your .change() function. This way you add a new binding everytime, hence the increasing number of calls to the function.

The proper way to do so is moving $('#questTable tbody').on('click', 'tr', function () { outside of GetQuestions.

Stefano Zanini
  • 5,876
  • 2
  • 13
  • 33
  • Yes you're right. I actually tryed it but i couldn't fire onclick anywhere else except ajax call, but now i understand why i couldn't and solved with McCrossan's advice. Thank you for the answer. – Flardryn Mar 08 '17 at 19:01
0

Every time you call $('selector').on('click', ...), you're registering a new callback to execute when an element matching your selector is clicked. So in this case, every time that ajax call completes, you will register another click handler. So if your ajax call executes three times, you will have registered three identical click handlers, and all of them will execute.

You should make sure your $('#questTable tbody').on('click', 'tr', ...) is only executed once.

metalex9
  • 86
  • 2
  • 5
  • Yes you're right. I couldn't fire onclick anywhere else except ajax call, but now i understand why i couldn't and solved with McCrossan's advice. Thank you for the answer. – Flardryn Mar 08 '17 at 19:00
0

You have add new event listener after every ajax request, move click event from ajax callback

//out of doc ready
function GetQuestions(questGroup) {
 $.ajax({
  type:'POST',
  dataType:'json',
  contentType:'application/json',
  url:'SetAudit.aspx/Questions',
  data: '{"q_group":"' + questGroup + '"}',
  success: function (result) {
   $('#questTable').DataTable({
    data: result.d,
    columns: [
     { data: 'q_id' },
     { data: 'q_text' }
    ]
   });


  }
 });
}

//this click function runs multiple time at 1 click
$('#questTable tbody').on('click', 'tr', function () {
 var table = $('#questTable').DataTable();
 var count = table.rows('.selected').count();
 $(this).toggleClass('selected');
 $('#selectedCount').text('' + table.rows('.selected').count() + '');
});
  • I actually tryed this one but now i get my real problem and solved it with McCrossan's advice. But thank you for the answer. – Flardryn Mar 08 '17 at 18:59