There is a bug in the columns.adjust()
function that can cause columns to extend past the scroll bar (if using scrollY
) or past the right edge of the table in some cases. This behavior will happen when using the style table-layout: fixed, allowing table cells to shrink smaller than the text they contain, and setting a column width to 0%. I have created a JS Bin showing the behavior as well as the reason it happens: live.datatables.net/rumomuze/1/edit. The reason being that setting the width of a th
to 0% has a different behavior than setting the width to 0px. When the cell is set to 0px it will still be rendered, consuming an amount of space determined by it's padding, margin and border. However, when the cell is set to 0% padding, margin and border are ignored and the column is not rendered at all.
The culprit of this bug is the function _fnCalculateColumnWidths. In this function the datatable DOM instance is cloned and the columns of the cloned table are set to the column property sWidthOrig. If one of the columns sWidthOrig is 0% the cloned table's column is set to 0%, then the original table columns are calculated in px from that. This means that the px width for the other columns (those that aren't 0% width) are calculated with the 0% column not being rendered. When the calculated widths are applied to the original table the 0% column is set to 0px and suddenly has some width that didn't exist when the other columns were calculated, thus the original table is now larger than expected and extends beyond it's right boundary. The fix I am using for this involves calculating a sum of the padding, margin and border for any columns that are set to 0% and removing this space from the size of the cloned table. See the excerpt below:
/**
* Calculate the width of columns for the table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
*/
function _fnCalculateColumnWidths ( oSettings )
{
...
// Apply custom sizing to the cloned header
headerCells = _fnGetUniqueThs(oSettings, tmpTable.find('thead')[0]);
var hiddenWidth= 0;
for (i = 0; i < visibleColumns.length; i++) {
column = columns[visibleColumns[i]];
if (/^0*\.?0*%$/.test(column.sWidthOrig)) {
var header = $(column.nTh);
hiddenWidth += header.width() === 0 ? header.outerWidth() :
(header.css("padding-left") + header.css("padding-right") + header.css("margin-left") +
header.css("margin-right") + header.css("border-left-width") + header.css("border-right-width"));
}
headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
_fnStringToCss(column.sWidthOrig) :
'';
...
// When scrolling (X or Y) we want to set the width of the table as
// appropriate. However, when not scrolling leave the table width as it
// is. This results in slightly different, but I think correct behaviour
if (scrollX && scrollXInner) {
tmpTable.width(scrollXInner);
}
else if (scrollX) {
tmpTable.css('width', 'auto');
tmpTable.removeAttr('width');
// If there is no width attribute or style, then allow the table to
// collapse
if (tmpTable.width() < tableContainer.clientWidth && tableWidthAttr) {
tmpTable.width(tableContainer.clientWidth);
}
}
else if (scrollY) {
tmpTable.width(tableContainer.clientWidth);
}
else if (tableWidthAttr) {
tmpTable.width(tableWidthAttr);
}
//Remove width that was displaced by columns with 0% width
tmpTable.width(tmpTable.width() - hiddenWidth);
...
}