diff --git a/css/fixedHeader.bootstrap.scss b/css/fixedHeader.bootstrap.scss index 1c1fa5c..a6badb2 100644 --- a/css/fixedHeader.bootstrap.scss +++ b/css/fixedHeader.bootstrap.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { @@ -7,8 +8,4 @@ table.dataTable.fixedHeader-locked { margin-bottom: 0 !important; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/fixedHeader.bootstrap4.scss b/css/fixedHeader.bootstrap4.scss index 1c1fa5c..a6badb2 100644 --- a/css/fixedHeader.bootstrap4.scss +++ b/css/fixedHeader.bootstrap4.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { @@ -7,8 +8,4 @@ table.dataTable.fixedHeader-locked { margin-bottom: 0 !important; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/fixedHeader.bootstrap5.scss b/css/fixedHeader.bootstrap5.scss index f828ef6..e6c43ab 100644 --- a/css/fixedHeader.bootstrap5.scss +++ b/css/fixedHeader.bootstrap5.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { @@ -13,8 +14,4 @@ div.dtfh-floatingparent-foot table { border-top-style: solid; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/fixedHeader.bulma.scss b/css/fixedHeader.bulma.scss index 1c1fa5c..91fe38d 100644 --- a/css/fixedHeader.bulma.scss +++ b/css/fixedHeader.bulma.scss @@ -1,14 +1,11 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { - position: relative !important; + position: relative !important; background-color: white; margin-top: 0 !important; margin-bottom: 0 !important; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/fixedHeader.dataTables.scss b/css/fixedHeader.dataTables.scss index ea0e9ea..0f772d6 100644 --- a/css/fixedHeader.dataTables.scss +++ b/css/fixedHeader.dataTables.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.fixedHeader-floating, @@ -9,8 +10,5 @@ table.fixedHeader-locked { background-color: var(--dt-html-background); } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); + diff --git a/css/fixedHeader.foundation.scss b/css/fixedHeader.foundation.scss index 1c1fa5c..a6badb2 100644 --- a/css/fixedHeader.foundation.scss +++ b/css/fixedHeader.foundation.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { @@ -7,8 +8,4 @@ table.dataTable.fixedHeader-locked { margin-bottom: 0 !important; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/fixedHeader.jqueryui.scss b/css/fixedHeader.jqueryui.scss index 240aa62..4f51b22 100644 --- a/css/fixedHeader.jqueryui.scss +++ b/css/fixedHeader.jqueryui.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { @@ -5,8 +6,4 @@ table.dataTable.fixedHeader-locked { background-color: white; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/fixedHeader.semanticui.scss b/css/fixedHeader.semanticui.scss index e386e37..c311765 100644 --- a/css/fixedHeader.semanticui.scss +++ b/css/fixedHeader.semanticui.scss @@ -1,3 +1,4 @@ +@use "sass:meta"; table.dataTable.fixedHeader-floating, table.dataTable.fixedHeader-locked { @@ -6,8 +7,4 @@ table.dataTable.fixedHeader-locked { border-bottom-width: 0 !important; } -@media print { - table.fixedHeader-floating { - display: none; - } -} +@include meta.load-css('print.scss'); diff --git a/css/print.scss b/css/print.scss new file mode 100644 index 0000000..c3ae229 --- /dev/null +++ b/css/print.scss @@ -0,0 +1,7 @@ + +@media print { + table.fixedHeader-floating, + table.fixedHeader-locked { + display: none; + } +} diff --git a/docs/event/fixedheader-mode.xml b/docs/event/fixedheader-mode.xml new file mode 100644 index 0000000..0ce42c9 --- /dev/null +++ b/docs/event/fixedheader-mode.xml @@ -0,0 +1,29 @@ + + + fixedheader-mode + The display mode of the fixed header / footer has changed + 4.1.0 + + + function( e, mode, item ) + + jQuery event object + + + Defines which mode the item is moving into. This can be one of: + + * `-string in-place` - in its non-floating position + * `-string in` - floating over the middle of the table + * `-string below` - used only for the header. The floating header is positioned at the bottom of the table body. + * `-string above` - used only for the footer. The floating footer is positioned at the top of the table body. + + + Denotes if the header or footer is the item that is changing its float state. Can be either `-string header` or `-string footer`. + + HTML table element + + + + This event is triggered when a scroll of the page by the end user causes the header / footer of the DataTable to change its fixed (floating) state. It can be useful to know this if you have extra information in the header / footer that needs to be rerendered depending on the fixed state. + + \ No newline at end of file diff --git a/examples/data/2500.txt b/examples/data/2500.txt index 4e9f706..7f5fed2 100644 --- a/examples/data/2500.txt +++ b/examples/data/2500.txt @@ -1,5 +1,5 @@ { "aaData": [ - [ "1", "Armand", "Warren", "56045", "Taiwan, Province of China" ], + [ "1", "Armand", "Warren", "56045", "Taiwan" ], [ "2", "Xenos", "Salas", "71090", "Liberia" ], [ "3", "Virginia", "Whitaker", "62723", "Nicaragua" ], [ "4", "Kato", "Patrick", "97662", "Palau" ], @@ -144,7 +144,7 @@ [ "143", "Geoffrey", "Byers", "85753", "Netherlands Antilles" ], [ "144", "Odette", "Sawyer", "55418", "Tunisia" ], [ "145", "Chaney", "Flowers", "F4W 7O7", "Maldives" ], - [ "146", "Shelly", "Glover", "M5Y 4A6", "Taiwan, Province of China" ], + [ "146", "Shelly", "Glover", "M5Y 4A6", "Taiwan" ], [ "147", "Uriel", "Thornton", "Z6Q 5B7", "Myanmar" ], [ "148", "Clio", "Nicholson", "Y8S 7P2", "Martinique" ], [ "149", "Jana", "Foley", "B1O 9J5", "United Arab Emirates" ], @@ -487,7 +487,7 @@ [ "486", "Buffy", "Sharpe", "H8F 8G6", "Georgia" ], [ "487", "Harrison", "Cross", "Y1A 1R8", "United Kingdom" ], [ "488", "Ursa", "Wolf", "J8C 9Q8", "French Polynesia" ], - [ "489", "Nayda", "Vasquez", "05523", "Taiwan, Province of China" ], + [ "489", "Nayda", "Vasquez", "05523", "Taiwan" ], [ "490", "Gretchen", "Walters", "28628", "Seychelles" ], [ "491", "Adrian", "Hickman", "17956", "El Salvador" ], [ "492", "Laura", "Moon", "32103", "Myanmar" ], @@ -623,7 +623,7 @@ [ "622", "Rama", "Perkins", "56506", "Russian Federation" ], [ "623", "Boris", "Chaney", "66737", "Antigua and Barbuda" ], [ "624", "Edward", "Clarke", "30722", "Iraq" ], - [ "625", "Skyler", "Wise", "53248", "Taiwan, Province of China" ], + [ "625", "Skyler", "Wise", "53248", "Taiwan" ], [ "626", "Uta", "Cox", "85242", "Malawi" ], [ "627", "Lesley", "Watkins", "26710", "Estonia" ], [ "628", "Gray", "Harrison", "C5L 9Y7", "Nepal" ], @@ -847,7 +847,7 @@ [ "846", "Gay", "Harper", "56404", "Virgin Islands, U.S." ], [ "847", "Joel", "Holman", "C1F 1C4", "Saint Lucia" ], [ "848", "Clayton", "Pennington", "57003", "Kazakhstan" ], - [ "849", "Susan", "Mckee", "I5U 8F2", "Taiwan, Province of China" ], + [ "849", "Susan", "Mckee", "I5U 8F2", "Taiwan" ], [ "850", "Jenna", "Stein", "P2K 6L4", "Reunion" ], [ "851", "Madonna", "Joyner", "Q4Q 4K6", "Guadeloupe" ], [ "852", "Deirdre", "Ingram", "N7U 3N9", "Monaco" ], @@ -999,7 +999,7 @@ [ "998", "Chloe", "Richards", "63091", "Canada" ], [ "999", "Uriel", "Snyder", "95487", "Pakistan" ], [ "1000", "Maite", "Cash", "90705", "Syrian Arab Republic" ], - [ "1001", "Cameron", "Schwartz", "82778", "Taiwan, Province of China" ], + [ "1001", "Cameron", "Schwartz", "82778", "Taiwan" ], [ "1002", "Faith", "Jimenez", "J6K 2P9", "Saint Pierre and Miquelon" ], [ "1003", "Otto", "Hancock", "34535", "Andorra" ], [ "1004", "Harlan", "Blackwell", "N8Y 4E6", "Qatar" ], @@ -1255,7 +1255,7 @@ [ "1254", "Aiko", "Brooks", "R6R 9E4", "Liberia" ], [ "1255", "Jacob", "Moore", "P2Y 6P3", "Cambodia" ], [ "1256", "Madeline", "Bishop", "D4I 2E7", "Dominican Republic" ], - [ "1257", "Jarrod", "Evans", "C9O 7V7", "Taiwan, Province of China" ], + [ "1257", "Jarrod", "Evans", "C9O 7V7", "Taiwan" ], [ "1258", "Beverly", "Witt", "64850", "Denmark" ], [ "1259", "Karyn", "Rhodes", "D6G 5Z3", "Cape Verde" ], [ "1260", "Imani", "Quinn", "01897", "Macao" ], @@ -1317,9 +1317,9 @@ [ "1316", "Fiona", "Bryant", "U7Y 7N6", "Cocos (Keeling) Islands" ], [ "1317", "Lenore", "Boyle", "H5G 6P9", "Sudan" ], [ "1318", "Ignacia", "Avila", "Y5M 1S2", "Romania" ], - [ "1319", "Wendy", "Stein", "25422", "Taiwan, Province of China" ], + [ "1319", "Wendy", "Stein", "25422", "Taiwan" ], [ "1320", "Garrison", "Bass", "B9J 6D9", "Romania" ], - [ "1321", "Curran", "Roy", "X2F 4P2", "Taiwan, Province of China" ], + [ "1321", "Curran", "Roy", "X2F 4P2", "Taiwan" ], [ "1322", "Oliver", "Beach", "N6J 1C5", "Kazakhstan" ], [ "1323", "Bo", "Duran", "D5C 5C2", "Eritrea" ], [ "1324", "Tashya", "Morrow", "N2J 7O9", "Rwanda" ], @@ -1801,7 +1801,7 @@ [ "1800", "Charity", "Dawson", "29508", "Greece" ], [ "1801", "Ulric", "Guzman", "I6R 6P6", "Micronesia" ], [ "1802", "Keefe", "Scott", "J1R 8T6", "Uzbekistan" ], - [ "1803", "Florence", "Price", "U7P 8F6", "Taiwan, Province of China" ], + [ "1803", "Florence", "Price", "U7P 8F6", "Taiwan" ], [ "1804", "Griffith", "England", "92557", "China" ], [ "1805", "Kay", "Nielsen", "85991", "Suriname" ], [ "1806", "Tamekah", "Blackburn", "47324", "Panama" ], @@ -1860,7 +1860,7 @@ [ "1859", "Yael", "Hester", "69399", "Hong Kong" ], [ "1860", "Hunter", "Harding", "M4O 6N5", "Bosnia and Herzegovina" ], [ "1861", "Breanna", "Sutton", "N2C 6K3", "Singapore" ], - [ "1862", "Bo", "Huffman", "54558", "Taiwan, Province of China" ], + [ "1862", "Bo", "Huffman", "54558", "Taiwan" ], [ "1863", "Zena", "Potts", "80326", "Czech Republic" ], [ "1864", "Lucian", "Sykes", "D4M 6M5", "Trinidad and Tobago" ], [ "1865", "Gabriel", "Shepherd", "77631", "Bahrain" ], @@ -2318,7 +2318,7 @@ [ "2317", "Nichole", "Stephens", "B8P 3D5", "Qatar" ], [ "2318", "Mary", "Dorsey", "J7D 1E5", "Iceland" ], [ "2319", "Yetta", "Dillon", "I7X 9D3", "Hong Kong" ], - [ "2320", "Hope", "May", "L5W 1T9", "Taiwan, Province of China" ], + [ "2320", "Hope", "May", "L5W 1T9", "Taiwan" ], [ "2321", "Daphne", "Barr", "W2B 9G2", "Korea, Republic of" ], [ "2322", "Melissa", "Hartman", "17607", "Reunion" ], [ "2323", "Acton", "Merritt", "U7M 3Q5", "Cape Verde" ], @@ -2368,7 +2368,7 @@ [ "2367", "Kylie", "Hansen", "38932", "Eritrea" ], [ "2368", "Iola", "Copeland", "P4X 9M4", "Rwanda" ], [ "2369", "Jorden", "Green", "48018", "Namibia" ], - [ "2370", "Hamish", "Porter", "L6F 8L1", "Taiwan, Province of China" ], + [ "2370", "Hamish", "Porter", "L6F 8L1", "Taiwan" ], [ "2371", "Ezra", "Taylor", "09148", "French Southern Territories" ], [ "2372", "Dara", "Pratt", "00558", "Saint Kitts and Nevis" ], [ "2373", "Oliver", "Holt", "C4N 5Z7", "Thailand" ], diff --git a/examples/options/show-hide.xml b/examples/options/show-hide.xml index e292e3c..0241d12 100644 --- a/examples/options/show-hide.xml +++ b/examples/options/show-hide.xml @@ -46,9 +46,11 @@ document.querySelector('#toggle').addEventListener('click', function () { diff --git a/js/dataTables.fixedHeader.js b/js/dataTables.fixedHeader.js index f319d28..f7b72d3 100644 --- a/js/dataTables.fixedHeader.js +++ b/js/dataTables.fixedHeader.js @@ -1,4 +1,4 @@ -/*! FixedHeader 4.0.0 +/*! FixedHeader 4.0.5 * © SpryMedia Ltd - datatables.net/license */ @@ -6,7 +6,7 @@ * @summary FixedHeader * @description Fix a table's header or footer, so it is always visible while * scrolling - * @version 4.0.0 + * @version 4.0.5 * @author SpryMedia Ltd * @contact datatables.net * @@ -74,20 +74,41 @@ var FixedHeader = function (dt, config) { tfoot: $(dt.table().footer()), header: { host: null, + scrollAdjust: null, floating: null, - floatingParent: $('
'), + floatingParent: $( + '
' + // location + '
' + // hidden overflow / scrolling + '
' + // adjustment for scrollbar (padding) + '
' + + '
'), + limiter: null, placeholder: null }, footer: { host: null, + scrollAdjust: null, floating: null, - floatingParent: $('
'), + floatingParent: $( + '
' + + '
' + + '
' + + '
' + + '
'), + limiter: null, placeholder: null } }; - this.dom.header.host = this.dom.thead.parent(); - this.dom.footer.host = this.dom.tfoot.parent(); + var dom = this.dom; + + dom.header.host = dom.thead.parent(); + dom.header.limiter = dom.header.floatingParent.children(); + dom.header.scrollAdjust = dom.header.limiter.children(); + + dom.footer.host = dom.tfoot.parent(); + dom.footer.limiter = dom.footer.floatingParent.children(); + dom.footer.scrollAdjust = dom.footer.limiter.children(); var dtSettings = dt.settings()[0]; if (dtSettings._fixedHeader) { @@ -222,6 +243,8 @@ $.extend(FixedHeader.prototype, { this._positions(); this._scroll(force !== undefined ? force : true); + this._widths(this.dom.header); + this._widths(this.dom.footer); }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -313,11 +336,10 @@ $.extend(FixedHeader.prototype, { else { if (itemDom.floating) { if (itemDom.placeholder !== null) { - itemDom.placeholder.remove(); + itemDom.placeholder.detach(); } - itemDom.floating.children().detach(); - itemDom.floating.remove(); + itemDom.floating.detach(); } var tableNode = $(dt.table().node()); @@ -330,14 +352,11 @@ $.extend(FixedHeader.prototype, { top: 0, left: 0 }) - .removeAttr('id') - .append(itemElement); + .removeAttr('id'); itemDom.floatingParent .css({ - width: scrollBody.width(), - overflow: 'hidden', - height: 'fit-content', + width: scrollBody[0].offsetWidth, position: 'fixed', left: scrollEnabled ? tableNode.offset().left + scrollBody.scrollLeft() @@ -359,32 +378,69 @@ $.extend(FixedHeader.prototype, { ? 'dtfh-floatingparent-foot' : 'dtfh-floatingparent-head' ) - .append(itemDom.floating) - .appendTo('body'); + .appendTo('body') + .children() + .eq(0); + + itemDom.limiter + .css({ + width: '100%', + overflow: 'hidden', + height: 'fit-content' + }); + + itemDom.scrollAdjust + .append(itemDom.floating); this._stickyPosition(itemDom.floating, '-'); var scrollLeftUpdate = function () { var scrollLeft = scrollBody.scrollLeft(); that.s.scrollLeft = { footer: scrollLeft, header: scrollLeft }; - itemDom.floatingParent.scrollLeft(that.s.scrollLeft.header); + itemDom.limiter.scrollLeft(that.s.scrollLeft.header); }; scrollLeftUpdate(); scrollBody.off('scroll.dtfh').on('scroll.dtfh', scrollLeftUpdate); + // Need padding on the header's container to allow for a scrollbar, + // just like how DataTables handles it + itemDom.scrollAdjust.css({ + width: 'fit-content', + paddingRight: that.s.dt.settings()[0].oBrowser.barWidth + }); + + // Blocker to hide the table behind the scrollbar - this needs to use + // fixed positioning in the container since we don't have an outer wrapper + let blocker = $( + item === 'footer' + ? 'div.dtfc-bottom-blocker' + : 'div.dtfc-top-blocker', + dt.table().container() + ); + + if (blocker.length) { + blocker + .clone() + .appendTo(itemDom.floatingParent) + .css({ + position: 'fixed', + right: blocker.width() + }); + } + // Insert a fake thead/tfoot into the DataTable to stop it jumping around itemDom.placeholder = itemElement.clone(false); itemDom.placeholder.find('*[id]').removeAttr('id'); + // Move the thead / tfoot elements around - original into the floating + // element and clone into the original table itemDom.host.prepend(itemDom.placeholder); + itemDom.floating.append(itemElement); + + this._widths(itemDom); - // Copy the `colgroup` element for widths - itemDom.placeholder - .parent() - .find('colgroup') - .clone() - .appendTo(itemDom.floating); + return scrollLeftUpdate; } }, @@ -406,19 +462,12 @@ $.extend(FixedHeader.prototype, { var potential; if (right !== 'auto' && !rtl) { - // New position either adds or dismisses the barWidth - potential = - +right.replace(/px/g, '') + - (sign === '-' ? -1 : 1) * - that.s.dt.settings()[0].oBrowser.barWidth; + potential = +right.replace(/px/g, '') $(this).css('right', potential > 0 ? potential : 0); } else if (left !== 'auto' && rtl) { - potential = - +left.replace(/px/g, '') + - (sign === '-' ? -1 : 1) * - that.s.dt.settings()[0].oBrowser.barWidth; + potential = +left.replace(/px/g, ''); $(this).css('left', potential > 0 ? potential : 0); } @@ -469,6 +518,7 @@ $.extend(FixedHeader.prototype, { * @private */ _modeChange: function (mode, item, forceChange) { + var dt = this.s.dt; var itemDom = this.dom[item]; var position = this.s.position; @@ -506,11 +556,13 @@ $.extend(FixedHeader.prototype, { itemDom.placeholder = null; } - if (item === 'header') { - itemDom.host.prepend(tablePart); - } - else { - itemDom.host.append(tablePart); + if (!$.contains(itemDom.host[0], tablePart[0])) { + if (item === 'header') { + itemDom.host.prepend(tablePart); + } + else { + itemDom.host.append(tablePart); + } } if (itemDom.floating) { @@ -520,6 +572,7 @@ $.extend(FixedHeader.prototype, { } if (itemDom.floatingParent) { + itemDom.floatingParent.find('div.dtfc-top-blocker').remove(); itemDom.floatingParent.remove(); } @@ -528,9 +581,9 @@ $.extend(FixedHeader.prototype, { ); } else if (mode === 'in') { - // Remove the header from the read header and insert into a fixed + // Remove the header from the real table and insert into a fixed // positioned floating table clone - this._clone(item, forceChange); + let scrollLeftUpdate = this._clone(item, forceChange); // Get useful position values var scrollOffset = scrollBody.offset(); @@ -571,11 +624,14 @@ $.extend(FixedHeader.prototype, { .css({ left: position.left, 'z-index': 3 - }) - .append(itemDom.floating); + }); importantWidth(position.width); + if (scrollLeftUpdate) { + scrollLeftUpdate(); + } + if (item === 'footer') { itemDom.floating.css('top', ''); } @@ -619,6 +675,8 @@ $.extend(FixedHeader.prototype, { this.s.scrollLeft.header = -1; this.s.scrollLeft.footer = -1; this.s[item + 'Mode'] = mode; + + dt.trigger('fixedheader-mode', [mode, item]); }, /** @@ -739,12 +797,17 @@ $.extend(FixedHeader.prototype, { forceChange = true; } else { - this.dom.header.floatingParent + var child = this.dom.header.floatingParent .css({ top: this.c.headerOffset, position: 'fixed' }) - .append(this.dom.header.floating); + .children() + .eq(0); + + if (child.find(this.dom.header.floating).length === 0) { + child.append(this.dom.header.floating); + } } } // Anything else and the view is below the table @@ -938,6 +1001,46 @@ $.extend(FixedHeader.prototype, { return true; } return false; + }, + + /** + * Realign columns by using the colgroup tag and + * checking column widths + */ + _widths: function (itemDom) { + if (! itemDom || ! itemDom.placeholder) { + return; + } + + // Match the table overall width + var tableNode = $(this.s.dt.table().node()); + var scrollBody = $(tableNode.parent()); + + itemDom.floatingParent.css('width', scrollBody[0].offsetWidth); + itemDom.floating.css('width', tableNode[0].offsetWidth); + + // Strip out the old colgroup + $('colgroup', itemDom.floating).remove(); + + // Copy the `colgroup` element to define the number of columns - needed + // for complex header cases where a column might not have a unique + // header + var cols = itemDom.placeholder + .parent() + .find('colgroup') + .clone() + .appendTo(itemDom.floating) + .find('col'); + + // However, the widths defined in the colgroup from the DataTable might + // not exactly reflect the actual widths of the columns (content can + // force it to stretch). So we need to copy the actual widths into the + // colgroup / col's used for the floating header. + var widths = this.s.dt.columns(':visible').widths(); + + for (var i=0 ; i, settings: boolean | ConfigFixedHeader): void; + new (dt: Api, settings: boolean | ConfigFixedHeader): DataTablesStatic['FixedHeader']; /** * FixedHeader version