Scroller behavior for variable-height table

Scroller behavior for variable-height table

EnigmaEnigma Posts: 2Questions: 0Answers: 0
edited April 2012 in DataTables 1.9
Hello Allan,

First off, thanks for a fantastic plugin; really impressive work. I recently switched out infinite scrolling in favor of the Scroller plug-in, but have experienced some unusual behavior for a variable-height table. When I scroll to the bottom of my table, it periodically fails to render the last few rows. My guess is that this is due to the way I have the sScrollY value configured, as the table may not be triggering the last re-render.

Table Settings:
[code]
"sScrollY": 800,
"bAutoWidth": false,
"bDeferRender": true,
"bFilter": true,
"bSort": true,
"fnRowCallback": function (nRow, aData) {
// Customer column class
jQuery("td:eq(4)", nRow).addClass("name_column");
},
"fnInitComplete": function () {
// Adjust scroll areas for table
adjustTable();
}
[/code]

If I understand the plug-in correctly, the sScrollY value of 800px is used for the initial scrolling calculation and represents 1/3 of the scrollable area (for a total of 2400px.) If every row in my table is set to a static height of 80px and I want to render 30 rows, then the height of the sScrollY should be 800. Each time the scroll breaks the 1600px mark, an additional 800 pixels are rendered, while the first 800 are "forgotten."

The resize function called during the InitComplete callback, used to adjust the display area of the table to match the container:
[code]
adjustTable = function () {
var wrapperHeight = jQuery("#BlockList_Table_wrapper").height();
var headerHeight = jQuery(".dataTables_scrollHead").height();
var infoPadding = 20;
var infoHeight = jQuery("#BlockList_Table_info").height() + infoPadding;

// Adjust scroll area
jQuery(".dataTables_scroll").height(wrapperHeight - infoHeight);
jQuery(".dataTables_scrollBody").height(wrapperHeight - headerHeight - infoHeight);

// Adjust column sizing
bController.bList.fnAdjustColumnSizing();
};
[/code]

I wouldn't think this resize would affect the scrolling calculation, but I could definitely be wrong. If you (or anyone reading this) could provide some insight, it would be much appreciated. I don't have a mock-up of the table on hand, but would be happy to explain further if my explanation wasn't clear enough.

Thanks in advance,
Quinn

Replies

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    > I recently switched out infinite scrolling in favor of the Scroller plug-in, but have experienced some unusual behavior for a variable-height table.

    This won't work I'm afraid. As noted on the Scroller "home page" ( http://datatables.net/extras/scroller/ ):

    > Note that rows in the table must all be the same height. Information in a cell which expands on to multiple lines will cause some odd behaviour in the scrolling.

    This blog post has more details about how Scroller works: http://datatables.net/blog/Introducing_Scroller_-_Virtual_Scrolling_for_DataTables

    Allan
  • EnigmaEnigma Posts: 2Questions: 0Answers: 0
    Hello Allan,

    Thanks for the quick response. Via CSS, I've set all rows to a static height, whereas the table itself is resizable. I'd expect the Scroller calculation would still be fine in this case?

    Thanks,
    Quinn
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Yes - as long as all rows are the same height, then that's absolutely fine.

    Allan
  • jcreadyjcready Posts: 44Questions: 0Answers: 0
    edited April 2012
    Hey Allen, I have a similar issue with Scroller's calculation of the visible portion of the table's height.

    I'm making a full page media library (like iTunes) and because I always want the application to fill the entire page I have two divs (#top and #middle) absolutely positioned. The #top div has [code]top: 0; right: 0; left: 0; height: 72px[/code] and the #middle div has [code]top: 72px; right: 0; bottom: 0; left: 0[/code] so that both divs completely fill the screen.

    My DataTable is inside #middle with [code]"yScroll" : $("#middle").height()+"px"[/code] but only WebKit seems to be correctly filling the entire page with rows. Firefox only appears to be filling about half of that space. I think it has to do with the fact that #middle is absolutely positioned and that it doesn't have an explicitly set height property. I'm guessing that's the issue because it worked fine when I wasn't absolutely positioning the table's wrapper, but it's kind of confusing because after looking through oTable.oOptions I can see a "y" property that is correctly set to 870px (the height of #middle). So it clearly is getting the correct height value, but it's not respecting it.

    Any ideas?
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Can you link me to your page so I can investigate a bit please?

    Do you have the elements as display:none on initial load?

    Allan
  • jcreadyjcready Posts: 44Questions: 0Answers: 0
    edited May 2012
    In the HTML I have a element with just the header row inside the . I am populating the DataTable's "aaData" with data stored in localStorage (if it exists). If the data does not exist in localStorage, I make an XHR request to the server and receive back an array of objects. I then parse the data myself into the aaData format I want, save that aaData to localStorage and finally initialize the DataTable with the "aaData" property set.

    Here is all the options I'm initializing it with:

    [code]dom.oTable = $("#list").dataTable({

    "aaData": Temp.aaData, // Arrays
    "aaSorting": [[0,'asc'], [2,'asc'], [1,'asc']],
    "aoColumnDefs": [
    { "sType": "song", "aTargets": [ 0, 1, 2, 3 ] },
    { "sType": "numeric", "aTargets": [ 4, 5, 6, 8 ] }
    ],
    "aoColumns": [
    { "sName": "artist", "sClass": "artist" },
    { "sName": "title", "sClass": "title" },
    { "sName": "album", "sClass": "album" },
    { "sName": "genre", "sClass": "genre", "bSortable": false },
    { "sName": "dev-origin-id", "sClass": "source", "bSearchable": false, "bSortable": false, "bVisible": true },
    { "sName": "length", "sClass": "length", "bSearchable": false, "bVisible": true, "fnRender": function (oObj) { return hhmmss(oObj.aData[oObj.iDataColumn]); }, "bUseRendered": false },
    { "sName": "playcount", "sClass": "plays", "bSearchable": false, "bVisible": true },
    { "sName": "idhash", "bSearchable": false, "bVisible": false },
    { "sName": "art", "bSearchable": false, "bVisible": false }
    ],

    "oScroller": { // Objects
    "serverWait": 100,
    "trace": false
    }

    "bScrollAutoCss": true, // Booleans
    "bDestroy": true,
    "bInfo": false,
    "bAutoWidth": true,
    "bDeferRender": true,
    "bProcessing": false,
    "bRetrieve": true,
    "bStateSave": true,

    "iScrollLoadGap": 100, // Integers
    "iDisplayLength": 200,

    "sDom": "RtrS", // Strings
    "sScrollY": $('#middle').height()+'px',
    // Functions
    "fnStateSave": function (oSettings, oData) { localStorage.setItem('oData', JSON.stringify(oData)); },
    "fnStateLoad": function (oSettings) { return JSON.parse(localStorage.getItem('oData')); },
    });[/code]

    The (LESS) CSS for the portions of the page:

    [code]@navbarHeight: 72px;

    #top {
    position: absolute;
    top: 0; right: 0; left: 0;
    height: @navbarHeight;
    box-shadow: 0 -1px 0 black inset, 0 -2px 0 rgba(255,255,255,.2) inset;
    }

    #middle {
    position: absolute;
    top: @navbarHeight; right: 0; bottom: 0; left: 0;

    div#list_wrapper { // DataTable wrapper
    width: 100%;
    height: 100%;

    .dataTables_scroll { // Scroller wrapper
    .box-sizing(border-box); // Adds vendor prefixes
    width: 100%;
    height: 100% !important // Override inline styles
    position: relative;
    }

    .dataTables_scrollHead { // Faux fixed header wrapper
    position: absolute;
    z-index: 99999;
    height: 33px;
    overflow: visible;

    .dataTables_scrollHeadInner { // Inner wrapper for header
    width: 100% !important; // Override inline styles
    }
    }

    .dataTables_scrollBody { // Real table with tbody content
    height: 100% !important // Override inline styles

    thead { display: none } // No need to have two header rows
    }

    td.plays, // Force correct widths for columns
    td.length,
    td.source { width: 6% !important; }
    td.genre { width: 10% !important; }
    td.album { width: 24% !important; }
    td.title { width: 24% !important; }
    td.artist { width: 24% !important; }

    th.plays, // Could do this in one step if DataTables
    th.length, // used s inside a
    th.source { width: 6% !important; } // by only setting the width on each
    th.genre { width: 10% !important; }
    th.album { width: 24% !important; }
    th.title { width: 24% !important; }
    th.artist { width: 24% !important; }
    }
    }[/code]
  • jcreadyjcready Posts: 44Questions: 0Answers: 0
    edited May 2012
    Still don't know why it's only rendering in half the available space on Firefox, but I made a one line change in Scroller.js within the fnMeasure function: [code]this.s.viewportHeight = $("#middle").height();[/code] Obviously, that isn't a solution worth committing, but it fixed my problem.
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Interesting - thanks for the feedback on this. Good to hear that you've got a fix hat works for you with this. I'll try to replicate the problem in my test environment and see if I can find a fix.

    Allan
This discussion has been closed.