Hiding rows that don't have data
Hiding rows that don't have data
Example here: http://www.gallerymodules.com/
At the top of the table are 3 checkboxes that allow a user to filter by source. The checkbox has a JS onclick that calls this function:
[code]function fnShowHide( iCol ) {
var oTable = $('#module_table').dataTable();
var bVis = oTable.fnSettings().aoColumns[iCol].bVisible;
oTable.fnSetColumnVis( iCol, bVis ? false : true );
}[/code]
However, in certain cases (for example, uncheck the "Codex" box, and look at the "about" module), the hiding of a row will result in an entry that has no source to download. What would be the best way to hide that row, and subsequently show it again if the checkbox is re-checked?
At the top of the table are 3 checkboxes that allow a user to filter by source. The checkbox has a JS onclick that calls this function:
[code]function fnShowHide( iCol ) {
var oTable = $('#module_table').dataTable();
var bVis = oTable.fnSettings().aoColumns[iCol].bVisible;
oTable.fnSetColumnVis( iCol, bVis ? false : true );
}[/code]
However, in certain cases (for example, uncheck the "Codex" box, and look at the "about" module), the hiding of a row will result in an entry that has no source to download. What would be the best way to hide that row, and subsequently show it again if the checkbox is re-checked?
This discussion has been closed.
Replies
If the row is being hidden without a new table draw, it's just a jQuery function. You would have to iterate through all your rows to find matching candidates for hiding, which will produce an array of TR objects. Then all you have to do is call jQuery's hide() on it.
If your click event (unchecking the box, for example) causes the table to draw, then in the fnRowCallback, check the relevant TD elements for content. So, if aData[4]="" or other relevant check. If the conditions are met for hiding the row (no content in the TD elements being checked), you can use jQuery to traverse up a level (.parent() or .closest() should do the trick) to get to the TR. Then just call a hide() on that row.
Come to think of it, there's a hybrid approach:
You could generate a series of "special case" utility classes to apply to the TR for later use. So during the draw, you would add a class "no_download" to rows in which no download is available. Then in your click function, you simply have to $('.no_download').hide();
If you get stuck, I could try to generate some sample code. More likely, Allan will know a better way of doing it. LOL!
[code]
$("#mytable img").click(function() {
// get handle to the current image (trashcan)
var img = $(this);
// gradually hide the parent row
img.parents("tr").fadeOut(function() {
// after the row is deleted, hide our tooltip using the tooltip API
img.data("tooltip").hide();
});
});
[/code]
I like that he faded out the row instead of hiding it. Just a nice visual touch that works well since jQuery's slideUp doesn't work consistently or smoothly with TR elements.
Not a drop-in piece of code but the basics are there. It even uses parents() in the way I suggested; you would just need to put something similar into your own custom function that first builds your array of TR elements and binds to your other click event (instead of adding click to the images).
1. If you really only need to load your data once per pageload, just load it ALL, and use any checkboxes to selectively filter out contents. You would use the 'hybrid' approach and apply special classes to rows that match certain descriptions, in order to facilitate hiding/showing. So like I mentioned earlier, instead of firing the hide from the callback, you just use it to add classes.
2. You can have the checkbox click force a redraw. ;-)
it appears that when calling 'show', jquery simple tags the style as 'display:block' which messes up the table display. Here is the code I have now:
[code]
function fnShowHide( iCol, table ) {
var oTable = (table == 'm') ? $('#module_table').dataTable() : $('#theme_table').dataTable();
var bVis = oTable.fnSettings().aoColumns[iCol].bVisible;
oTable.fnSetColumnVis( iCol, bVis ? false : true );
fnToggleRows(iCol, oTable, bVis);
}
function fnToggleRows(iCol, oTable, bVis) {
var numrows = oTable.fnGetData().length;
for(i=0;i
So show() probably won't ever work, which is silly... not sure why they don't just have the function query for display type and then toggle as need be.
To get the row to display again properly, swap out the show() in favour of this:
[code]
$(node).css('display', 'table-row');
[/code]
To fix the striping I think would mean dropping the DataTables/jQuery UI way, which is to add the classes "odd" and "even" to the row. I'm not sure how to disable setting a class (passing "" to the sStripOdd and sStripEven parameters?), but once that was done you would instead style with tr:nth-child(even) and tr:nth-child(odd), which as far as I know will respect even and odd even when rows are removed.
supposedly, fnDraw will redraw the table, but it looks like it doesn't take the row visibility into account to re-stripe.
i actually had even/odd selectors added during the creation of the table (all done via php); i suppose that i could just start at the top and re-class each row to a new style. that seems inefficient to me though, as i'm already looping through the table once to figure out what rows to show/hide, then i'd have to do it again to get the style correct...though i suppose with a counter and some mod math, i could figure out whether the row's style needs to be updated...
I actually was wrong. Removing a row will NOT allow the nth-child selector to respect the striping, because (of course!) those rows are still in the DOM. Back to the drawing board!
Allan
is there a way to call $.fn.dataTableExt.afnFiltering from within the showhide function I have in place to handle the checkbox toggles? (sorry if that's a dumb JS question, but this is my first major journey into JS in years).
Edit to add:
here's the function:
[code]function fnShowHide( iCol, table ) {
var oTable = (table == 'm') ? $('#module_table').dataTable() : $('#theme_table').dataTable();
var bVis = oTable.fnSettings().aoColumns[iCol].bVisible;
oTable.fnSetColumnVis( iCol, bVis ? false : true );
} [/code]
The checkboxes call this function via onClick...
Edit to add #2:
The added wrinkle here is that I need to know which columns I have to look at, based on which checkbox is clicked, in order to determine how to filter the rows...my head is spinning now :)
1. object - DataTables settings object
2. array - data for the row in question. Array is indexed by column
3. int - index of row in aoData
So you know which columns (data columns) you want to look at, and you know which columns are needed based on the checkbox values. So there will need to be some logic there to combine the two :-)
Allan
Edited to delete the question re: multiple tables...got that figured out, now just need to know which checkbox was toggled...
[code]function fnShowHide( iCol, table ) {
var oTable = (table == 'm') ? $('#module_table').dataTable() : $('#theme_table').dataTable();
var bVis = oTable.fnSettings().aoColumns[iCol].bVisible;
oTable.fnSetColumnVis( iCol, bVis ? false : true );
oTable.fnDraw();
}
$.fn.dataTableExt.afnFiltering.push(
function( oSettings, aData, iDataIndex ) {
var codex = (document.getElementById('cbx_codex').checked) ? true : false;
var g30 = (document.getElementById('cbx_git30').checked) ? true : false;
var g31 = (document.getElementById('cbx_git31').checked) ? true : false;
if(oSettings.sTableId == "module_table") {
if(codex == false && aData[3] == '' && aData[4] == '') {
return false;
}
if(g30 == false && aData[2] == '' && aData[4] == '') {
return false;
}
if(g31 == false && aData[2] == '' && aData[3] == '') {
return false;
}
return true;
}
if(oSettings.sTableId == "theme_table") {
return true;
}
}
);[/code]
if you uncheck two boxes, a handful of the resulting list will still appear even if there is no download available. I must be missing a few checks in the "if tree" in the filter, but my brain is fried....any pointers would be appreciated.
[code]$.fn.dataTableExt.afnFiltering.push(
function( oSettings, aData, iDataIndex ) {
var codex = (document.getElementById('cbx_codex').checked) ? true : false;
var g30 = (document.getElementById('cbx_git30').checked) ? true : false;
var g31 = (document.getElementById('cbx_git31').checked) ? true : false;
if(oSettings.sTableId == "module_table") {
if(codex == false) {
if(aData[3] == '' && aData[4] == '') { return false; }
if(g30 == false && aData[4] == '') { return false; }
if(g31 == false && aData[3] == '') { return false; }
}
if(g30 == false) {
if(aData[2] == '' && aData[4] == '') { return false; }
if(codex == false && aData[4] == '') { return false; }
if(g31 == false && aData[2] == '') { return false; }
}
if(g31 == false) {
if(aData[2] == '' && aData[3] == '') { return false; }
if(codex == false && aData[3] == '') { return false; }
if(g30 == false && aData[2] == '') { return false; }
}
return true;
}
if(oSettings.sTableId == "theme_table") {
return true;
}
}
);[/code]
It may not be the most elegant way to do it, but it does what I want it to do. Props to Allan and Greg for their help on this endeavor.
Nice one - good to hear you got it going :-). The logic looks fine to me - often these things can be a little messy, but I think that's plenty understandable.
The one thing I would say is that there is an optimisation which can be made with regard to getting the filtering values from the DOM - which will be by far the slowest operation in your code, and will happen on every single draw. What you could do is cache the values and read them only once per draw:
[code]
var iDraw = -1;
var filters = {
codex: null,
g30: null,
g31: null
};
$.fn.dataTableExt.afnFiltering.push(
function( oSettings, aData, iDataIndex ) {
var codex, g30, g31;
if ( iDraw != oSettings.iDraw ) {
filters.codex = (document.getElementById('cbx_codex').checked) ? true : false;
filters.g30 = (document.getElementById('cbx_git30').checked) ? true : false;
filters.g31 = (document.getElementById('cbx_git31').checked) ? true : false;
}
if(oSettings.sTableId == "module_table") {
if(filters.codex == false) {
if(aData[3] == '' && aData[4] == '') { return false; }
if(filters.g30 == false && aData[4] == '') { return false; }
if(filters.g31 == false && aData[3] == '') { return false; }
}
if(filters.g30 == false) {
if(aData[2] == '' && aData[4] == '') { return false; }
if(filters.codex == false && aData[4] == '') { return false; }
if(filters.g31 == false && aData[2] == '') { return false; }
}
if(filters.g31 == false) {
if(aData[2] == '' && aData[3] == '') { return false; }
if(filters.codex == false && aData[3] == '') { return false; }
if(filters.g30 == false && aData[2] == '') { return false; }
}
return true;
}
if(oSettings.sTableId == "theme_table") {
return true;
}
}
);
[/code]
It uses a global variable - although you could scope limit that if you want, or even attach it to the DataTables object.
Regards,
Allan
Allan