17

I'm using the DataTables jQuery Plugin and have a click handler setup on row click as follows:

$('#dt tbody tr').click(function () {
        alert('e');
});

This works perfectly for the first page of DataTables results.

However, when I move to another page of results, the click handler no longer registers at all.

My presumption is that the DataTables code is stopping the propogation of the click event to my handler, but as this is only occurring on pages after the first it seems unusual.

As such, has anyone:

  1. Encountered (and ideally resolved) this problem
  2. Found a good way to track jQuery/JS event propogation to isolate why the event is being stopped

Cheers

2
  • My presumption was incorrect. Another unstated presumption: that binding in ajaxComplete would bind to all rows, was invalid as DataTables is wise and does not render all rows in the browser until required. As such, Kon's answer of live() is correct. Commented May 13, 2011 at 0:08
  • Please look at the answer provided by Chris Everitt. It uses a built-in DataTables function and DOES NOT use deprecated jquery functions. Commented Jul 16, 2013 at 12:13

5 Answers 5

12

I'm guessing the event handler binding is being applied only to the initially loaded rows. But once the row collection gets re-rendered in the markup, the event handlers are no more.

Check out jQuery's live() function. The key there being that event handlers are bound for all elements meeting selector criteria "now and in the future."

Sign up to request clarification or add additional context in comments.

5 Comments

Right of course, cheers. Odd it worked on the first page (all rows are loaded dynamically) but nonetheless I think this is the answer. I'll confirm and accept accordinging shortly.
That makes sense if you load the rows BEFORE defining the click() handler.
Of course - I had put the click in my ajaxComplete handler thinking that DataTables was loading all the rows and just hiding them. However, that only attaches to the first page because that's all the DataTables renders (the rest being kept in memory). Making it $('#dt tbody tr').live('click', function () etc. fixes it (and in fact can be declared outside the ajaxComplete because of the "now and in the future" bit you mention.
Just to note that live() has been deprecated and you should now use on() instead (for jquery > 1.7). See api.jquery.com/on
for me on() does not seem to work ... I just replace live() call with on() function but it does not work ... any caveats?
11

I had this problem on a single page application. The live method worked for me except after a postback. My table was populated via ajax, and the user could cause it to be destroyed and recreated.

To fix it I used dataTables.$: "http://datatables.net/api#$"

Here is my fix using the example DataTables give for the hidden row function.

$(document).ready(function() {
    /*
    * Insert a 'details' column to the table
    */
    var nCloneTh = document.createElement( 'th' );
    var nCloneTd = document.createElement( 'td' );
    nCloneTd.innerHTML = '<img src="../examples_support/details_open.png">';
    nCloneTd.className = "center";

    /* CHANGE: Remove all the expand control elements we may have added earlier
    * or else you'll add a new column for every postback
    */
    $('.expand-control').remove();

    /*
    * CHANGE: Add the expand-control class to these elements,
    * so we can remove them after a postback
    */
    $(nCloneTh).addClass('expand-control');
    $(nCloneTd).addClass('expand-control');

    $('#example thead tr').each( function () {
        this.insertBefore( nCloneTh, this.childNodes[0] );
    } );

    $('#example tbody tr').each( function () {
        this.insertBefore(  nCloneTd.cloneNode( true ), this.childNodes[0] );
    } );

    /*
    * Initialse DataTables, with no sorting on the 'details' column
    */
    var oTable = $('#example').dataTable( {
        "aoColumnDefs": [
            { "bSortable": false, "aTargets": [ 0 ] }
        ],
        "aaSorting": [[1, 'asc']]
    });

    /* Add event listener for opening and closing details
    * Note that the indicator for showing 
    * which row is open is not controlled by DataTables,
    * rather it is done here
    */

    /* CHANGE:  Here I use jQuery.dataTable.$ instead of
    * jQuery('#example tbody td img'),
    * this is what preserves the event handler on the 2nd (etc) 
    * pages after a postback
    * Note the use of on instead of live, recommended over live as of 1.7 
    */
    oTable.$('tr').find('img').on('click', function () {
        var nTr = $(this).parents('tr')[0];
        if ( oTable.fnIsOpen(nTr) )
        {
            /* This row is already open - close it */
            this.src = "../examples_support/details_open.png";
            oTable.fnClose( nTr );
        }
        else
        {
            /* Open this row */
            this.src = "../examples_support/details_close.png";
            oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
        }
    } );
} );

2 Comments

This should be the accepted answer. Using deprecated functions like jquery live() is never a good idea. Just use tableVariable.$('tr') instead of $('#tableId tr') like you said. +1
This also solved my similar issue when trying to use a technique suggested on this blog post: ricardocovo.com/2010/09/02/… (i.e., using the delete confirmation dialog technique in conjunction with jQuery datatables); ... the delete links stopped working on pages after the first page; so instead of using $('delete-link').click( function () etc ... , I used the idea above to fix the problem, by saying: oTable.$('.delete-link').on('click', function() { ... }; at that point the delete links started working again.
5

I had the same issue with buttons in all of my DataTables rows, the click event didn't work on any buttons after the first page of results. Kon gave the correct analysis (thank you Kon), but for those looking for example code, here's what worked for me:

$('.myButton').live('click', function() {
    var id = $(this).closest("tr").attr("id");
    var string = 'div_id=' + id;
    alert(string);
       // string sent to processing script here
});

Hope that helps!

Comments

3

Since live is now deprecated, i suggest using '.on'.

This should solve your problem:

$(document).on('click', '.myButton', function() {
    var id = $(this).closest("tr").attr("id");
    var string = 'div_id=' + id;
    alert(string);
       // string sent to processing script here
});

You can exchange document with some parent element as it is not very efficient. Perhaps try using a div containing your table.

Comments

1

My answer is similar to that of @Chris Everitt, with a slight difference. Just for those who would like to see it.. here it goes..

 var oTable = $('#masterTable').dataTable( {

                "aLengthMenu": [[5,10, 25, 50, 100 , -1], [5,10, 25, 50, 100, "All"]],
                "iDisplayLength" : 10,
                "aoColumnDefs": [
                             {"sWidth": "25%", "aTargets": [ 0 ] },
                             {"sWidth": "10%", "aTargets": [ 1 ] },
                             {"sWidth": "10%", "aTargets": [ 2 ] },
                             {"sWidth": "10%", "aTargets": [ 3 ] },
                             {"sWidth": "10%", "aTargets": [ 4 ] },
                             {"sWidth": "10%", "aTargets": [ 5 ] },
                             {"sWidth": "15%", "aTargets": [ 6 ] },

                             {"sClass": "align-left" , "aTargets": [ 0,1,4, 2,3,5,6] }              
                         ],

            "aoColumns": [
              { "bSortable": true },
              null, null, null,null, null, 
              { "bSortable": true }
            ]

            });

Registering event for all img (dom attr's) in the table -

 oTable.$('td').each( function () {

                    $(this).on('click','img', function () {
                        var nTr = $(this).parents('tr')[0];
                        if ( oTable.fnIsOpen(nTr) )
                        {
                            /* This row is already open - close it */
                            this.src = "${pageContext.request.contextPath}/theme/v_1_0/app-images/details_open.png";
                            oTable.fnClose( nTr );
                        }
                        else
                        {
                            /* Open this row */
                            this.src = "${pageContext.request.contextPath}/theme/v_1_0/app-images/details_close.png";



                            var html = '<div> Placeholder here.. </div>';

                            oTable.fnOpen(nTr, html, 'details');

                           }
                    } );    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.