Scroll DataTable to Bottom

Scroll DataTable to Bottom

edited January 2012 in General Posts: 41
I'm new to JQuery, forgive me for any obvious errors. I'm using JQuery UI, for tabs, and JQuery Datatables to load a log within a tab. The log contains HTML, table rows and data. The table:

<div id="tabs-1"> <table id="box-table-a" style="overflow: auto;"> <thead> <tr><th>Status</th><th>Process</th><th>Time</th><th>&nbsp;&nbsp;Message</th></tr> </thead> <tbody id="statusbox"> <tr><td><img src="/htmlFiles/StatusIcons/forbidden_32.png" /></td><td>Monitor Startup</td><td>xx/xx/xxxx xx:xx am </td><td>Placeholder value for monitor log.</td></tr> </tbody> </table> <br /> <form action="http://<!--SERVERURL-->/status">; <input type="hidden" name="restart" value="restart" /> <input type="submit" value="Restart log" /><span style="padding-left:10px;">Save the current log contents to a file, and start a new, empty log file.</span> </form> </div>
Note this is in a div for JQuery UI, and that the actual dynamic content is in the tbody.

The tbody is replaced with AJAX:

<script type="text/javascript"> // jQuery Document $(document).ready(function(){ $('#box-table-a').dataTable( { "sDom": 'lfrt;' , "sScrollY": "510px", "bScrollCollapse": false, "bPaginate": false, "bJqueryUI": true, "bFilter": false, "bAutoWidth": false, "aoColumns": [null, null, null, { "sWidth": "80%" }] } ); function loadLog(){ var oldscrollHeight = $("#statusbox").attr("scrollHeight") - 20; $.ajax({ url: "/htmlFiles/log.html", cache: false, success: function(html){ $("#statusbox").html(html); //Insert log into the #statusbox div var newscrollHeight = $("statusbox").attr("scrollHeight") - 20; $("#statusbox").animate({ scrollTop: newscrollHeight }, 'normal'); }, }); } setInterval (loadLog, 1500); }); </script>
This all works and looks great save for one issue: the table doesn't automatically scroll to the bottom. I had though the loadLog function would do this via the .animate method, but the newscrollHeight always contains NaN.

What am I doing wrong?

Thanks.

Replies

  • Posts: 22,089
    The tbody is replaced with AJAX:

    This will not work with DataTables. Any sorting / filtering etc would occur not he old data since you haven't told DataTables that there is new data for it to consider. You need to use the API if you want to replace the data in the table.

    Having said that, what is:

    $("statusbox").attr("scrollHeight")
    If newscrollHeight is NaN, then I'm going to guess the scrollHeight attribute is null.

    Allan
  • Well, you're obviously right, because the scrolling / height isn't working. Simplifying my code somewhat for the AJAX section:
    function loadLog(){ $.ajax({ url: "/htmlFiles/log.html", cache: false, success: function(html){ $("#statusbox").html(html); }, }); }
    I get exactly the results I expect: on page load, the datatable renders. 1.5 seconds later, the tbody is replaced with the new content from the AJAX function, and the table looks perfect. New content, scrollbar, etc. The only issue I have is that it doesn't scroll to the bottom.

    I thought I needed to add a reference to the datatable within my loadlog() function and perform a scrollto(), but reading your reply tells me that my approach isn't compatible with datatables, and likely only works because I'm not using any pagination, TableTools, sorting, etc.

    Instead of replacing the table contents with jquery ajax, as I've done, I need to build the datatable from the start with an AJAX data source.

    If you can provide me with a link that describes how to create a datatable where the contents are loaded (and regularly reloaded) from a non-JSON datasource, I'd appreciate it.

    Thanks for the reply.
  • edited January 2012 Posts: 41
    Ok, I've updated my code to use datatables with AJAX and a JSON data source. I've searched the forum and found the fnReloadAjax plugin, which I've added to my code.

    The HTML markup:
    <div id="tabs"> <ul> <li><a href="#tabs-1">Status Monitor</a></li> <li><a href="#tabs-2">Job Launcher</a></li> </ul> <div id="tabs-1"> <table id="box-table-a"> <thead> <tr><th>Status</th><th>Process</th><th>Time</th><th>&nbsp;&nbsp;Message</th></tr> </thead> <tbody id="statusbox"> </tbody> </table> <br /> </div> <div id="tabs-2"> <div id="accordion"><!-- TRIGGERS.HTML --> </div> </div> </div>
    Followed by the script to create the fnReloadAjax() function, load the datatable, and set a timed reload:

    $.fn.dataTableExt.oApi.fnReloadAjax = function ( oSettings, sNewSource, fnCallback, bStandingRedraw ) { if ( typeof sNewSource != 'undefined' && sNewSource != null ) { oSettings.sAjaxSource = sNewSource; } this.oApi._fnProcessingDisplay( oSettings, true ); var that = this; var iStart = oSettings._iDisplayStart; oSettings.fnServerData( oSettings.sAjaxSource, [], function(json) { /* Clear the old information from the table */ that.oApi._fnClearTable( oSettings ); /* Got the data - add it to the table */ var aData = (oSettings.sAjaxDataProp !== "") ? that.oApi._fnGetObjectDataFn( oSettings.sAjaxDataProp )( json ) : json; for ( var i=0 ; i<aData.length ; i++ ) { that.oApi._fnAddData( oSettings, aData[i] ); } oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); that.fnDraw(); if ( typeof bStandingRedraw != 'undefined' && bStandingRedraw === true ) { oSettings._iDisplayStart = iStart; that.fnDraw( false ); } that.oApi._fnProcessingDisplay( oSettings, false ); /* Callback user function - for event handlers etc */ if ( typeof fnCallback == 'function' && fnCallback != null ) { fnCallback( oSettings ); } }, oSettings ); } var myTable; $(document).ready(function(){ myTable = $('#box-table-a').dataTable( { "sDom": 'lfrt;' , "sScrollY": "510px", "sAjaxSource": '/htmlFiles/log.txt', "bPaginate": false, "bFilter": false, "bAutoWidth": false, "aoColumns": [null, null, null, { "sWidth": "80%" }] } ); function loadLog() { myTable.fnReloadAjax(); } setInterval (loadLog, 1500); });
    This has to annoying issue though, of every time the data source is reloaded, the table scrolls back up to the top.

    All of which brings me full circle back to my original question: how do I automatically scroll to the bottom row of the datatable when it loads?

    Thanks.
  • An update. I modified the fnReload Ajax function to store the current scrollTop() and set it after the table is drawn.

    $.fn.dataTableExt.oApi.fnReloadAjax = function ( oSettings, sNewSource, fnCallback, bStandingRedraw ) { if ( typeof sNewSource != 'undefined' && sNewSource != null ) { oSettings.sAjaxSource = sNewSource; } this.oApi._fnProcessingDisplay( oSettings, true ); var that = this; var iStart = oSettings._iDisplayStart; var scrollPos=$(".dataTables_scrollBody").scrollTop(); oSettings.fnServerData( oSettings.sAjaxSource, [], function(json) { /* Clear the old information from the table */ that.oApi._fnClearTable( oSettings ); /* Got the data - add it to the table */ var aData = (oSettings.sAjaxDataProp !== "") ? that.oApi._fnGetObjectDataFn( oSettings.sAjaxDataProp )( json ) : json; for ( var i=0 ; i<aData.length ; i++ ) { that.oApi._fnAddData( oSettings, aData[i] ); } oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); that.fnDraw(); if ( typeof bStandingRedraw != 'undefined' && bStandingRedraw === true ) { oSettings._iDisplayStart = iStart; that.fnDraw( false ); } $(".dataTables_scrollBody").scrollTop(scrollPos); that.oApi._fnProcessingDisplay( oSettings, false ); /* Callback user function - for event handlers etc */ if ( typeof fnCallback == 'function' && fnCallback != null ) { fnCallback( oSettings ); } }, oSettings ); }
    Now when the table reloads, the scroll position remains wherever the user had it. I also added a hidden column to the table, a timestamp, and set the table to sort descending on that column. That way, by default, new log entries appear at the top of the table. If the user has scrolled, they'll still be at that scroll position as new entries are loaded.

    Very elegant, exactly what I was after, and some diligent forum searching yielded all the answers.

    Thanks.
  • Posts: 22,089
    Excellent stuff - thanks for posting your solution code. I'll look at integrating that into the fnReloadAjax plug-in offered on the site (possibly using the standing redraw parameter as a flag to optionally enable it when scrolling is enabled).

    Allan
This discussion has been closed.