October 22, 2024
Chicago 12, Melborne City, USA
jQuery

Problem with DataTables.net FixedHeader Headers Shifting On Scroll


I worked with ChatGPT 4o to create a complex DataTables.net proof of concept based on the needs of my client, which includes the following DataTables.net plugins and functionality:

  • FixedHeader
  • Row Grouping
  • Child Rows
  • Row Footer
  • Multi select and select all

In the example code/jsfiddle provided, each of these are highlighted with a different color for easy identification.

Everything works perfectly. There’s just one issue with the Fixed Headers. When scrolling down and up, the fixed headers align perfectly. However, when you scroll up and reach the top of the page/view, the Fixed Headers shift left and are misaligned with the columns below. (see the Position column)

You can see the issue in this jsfiddle: https://jsfiddle.net/v5fs3do6/

Before (working)
enter image description here

After (headers shifted)
enter image description here

I have spent hours trying to resolve this with chatgpt, but the example seems to be too complex for it to resolve. We have tried rolling back certain plugins to try and see where/when the issue is introduced, but that has failed too.

Unfortunately, the requirements necessitate all of this functionality and plugins.

I am hoping the eyes of a human expert in front-end design/coding will be able to help me resolve this issue.

The jsfiddle has a working example demonstrating all the functionality and the issue, but here is the code as well.

Any help would be greatly appreciated and save me from going insane.
Thanks!

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DataTable with Grouping, Expanded Child Rows</title>
    <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
    <link rel="stylesheet" href="https://cdn.datatables.net/fixedheader/3.4.0/css/fixedHeader.dataTables.min.css">
    <link rel="stylesheet" href="https://cdn.datatables.net/rowgroup/1.4.0/css/rowGroup.dataTables.min.css">
    <style>
        table.dataTable thead th {
            background-color: #f4f4f4;
        }

        .group-header {
            font-weight: bold;
            background-color: #d8d8d8;
        }

        .child-row td {
            padding: 10px 0;
            border-top: none;
            text-align: left;
        }

        .blueTable th {
            background-color: #007bff !important;
            color: white !important;
        }

        .groupRow {
            background-color: #0056b3 !important;
            color: white !important;
        }

        .childDataRow {
            background-color: #80c1ff !important;
            color: black !important;
        }

        .groupFooterRow {
            background-color: #cce5ff !important;
            font-weight: bold;
            color: black !important;
        }
    </style>
</head>

<body>

    <h3>Select Grouping Column:</h3>
    <select id="group-by-column">
        <option value="none">None</option>
        <option value="office">Office</option>
        <option value="position">Position</option>
    </select>

    <table id="example" class="display nowrap table responsive-table table-vcenter table-hover" style="width:100%">
        <thead class="text-white">
            <tr class="bg-primary">
                <th><input type="checkbox" id="select-all"></th>
                <th>Name</th>
                <th>Position</th>
                <th>Age</th> 
                <th>Start date</th>
                <th>Salary</th>
                <th>Department</th>
                <th>Project</th>
                <th>Experience (Years)</th>
                <th>Location</th>
                <th class="d-none">Office</th>
                <th class="d-none">Hobby</th>
                <th class="d-none">Favorite Animal</th>
            </tr>
        </thead>
        <tbody>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Tiger Nixon
                    <input type="hidden" class="hobby" value="Fishing">
                    <input type="hidden" class="favorite-animal" value="Tiger">
                </td>
                <td>System Architect</td>
                <td>61</td>
                <td>2011/04/25</td>
                <td>$320,800</td>
                <td>IT</td>
                <td>Project A</td>
                <td>10</td>
                <td>Scotland</td>
                <td>Edinburgh</td>
                <td>Fishing</td>
                <td>Tiger</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Garrett Winters
                    <input type="hidden" class="hobby" value="Gardening">
                    <input type="hidden" class="favorite-animal" value="Cat">
                </td>
                <td>Accountant</td>
                <td>63</td>
                <td>2011/07/25</td>
                <td>$170,750</td>
                <td>Finance</td>
                <td>Project B</td>
                <td>8</td>
                <td>Japan</td>
                <td>Tokyo</td>
                <td>Gardening</td>
                <td>Cat</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Ashton Cox
                    <input type="hidden" class="hobby" value="Painting">
                    <input type="hidden" class="favorite-animal" value="Dog">
                </td>
                <td>Junior Technical Author</td>
                <td>66</td>
                <td>2009/01/12</td>
                <td>$86,000</td>
                <td>Documentation</td>
                <td>Project C</td>
                <td>5</td>
                <td>USA</td>
                <td>San Francisco</td>
                <td>Painting</td>
                <td>Dog</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Ryan Dixon
                    <input type="hidden" class="hobby" value="Fishing">
                    <input type="hidden" class="favorite-animal" value="Tiger">
                </td>
                <td>System Architect</td>
                <td>61</td>
                <td>2011/04/25</td>
                <td>$320,800</td>
                <td>IT</td>
                <td>Project D</td>
                <td>15</td>
                <td>Japan</td>
                <td>Tokyo</td>
                <td>Fishing</td>
                <td>Tiger</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Brielle Williamson
                    <input type="hidden" class="hobby" value="Photography">
                    <input type="hidden" class="favorite-animal" value="Dolphin">
                </td>
                <td>Integration Specialist</td>
                <td>61</td>
                <td>2012/12/02</td>
                <td>$372,000</td>
                <td>Development</td>
                <td>Project E</td>
                <td>9</td>
                <td>New Zealand</td>
                <td>Wellington</td>
                <td>Photography</td>
                <td>Dolphin</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Herrod Chandler
                    <input type="hidden" class="hobby" value="Hiking">
                    <input type="hidden" class="favorite-animal" value="Eagle">
                </td>
                <td>Sales Assistant</td>
                <td>59</td>
                <td>2012/08/06</td>
                <td>$137,500</td>
                <td>Sales</td>
                <td>Project F</td>
                <td>12</td>
                <td>UK</td>
                <td>London</td>
                <td>Hiking</td>
                <td>Eagle</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Rhona Davidson
                    <input type="hidden" class="hobby" value="Reading">
                    <input type="hidden" class="favorite-animal" value="Panda">
                </td>
                <td>Senior Javascript Developer</td>
                <td>55</td>
                <td>2010/10/14</td>
                <td>$327,900</td>
                <td>Development</td>
                <td>Project G</td>
                <td>7</td>
                <td>Canada</td>
                <td>Toronto</td>
                <td>Reading</td>
                <td>Panda</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Colleen Hurst
                    <input type="hidden" class="hobby" value="Cooking">
                    <input type="hidden" class="favorite-animal" value="Elephant">
                </td>
                <td>Javascript Developer</td>
                <td>39</td>
                <td>2009/09/15</td>
                <td>$205,500</td>
                <td>Development</td>
                <td>Project H</td>
                <td>6</td>
                <td>Australia</td>
                <td>Sydney</td>
                <td>Cooking</td>
                <td>Elephant</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Sonya Frost
                    <input type="hidden" class="hobby" value="Knitting">
                    <input type="hidden" class="favorite-animal" value="Fox">
                </td>
                <td>Software Engineer</td>
                <td>23</td>
                <td>2008/12/13</td>
                <td>$103,600</td>
                <td>IT</td>
                <td>Project I</td>
                <td>4</td>
                <td>USA</td>
                <td>San Diego</td>
                <td>Knitting</td>
                <td>Fox</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Jena Gaines
                    <input type="hidden" class="hobby" value="Traveling">
                    <input type="hidden" class="favorite-animal" value="Whale">
                </td>
                <td>Office Manager</td>
                <td>30</td>
                <td>2008/12/19</td>
                <td>$90,560</td>
                <td>Administration</td>
                <td>Project J</td>
                <td>2</td>
                <td>USA</td>
                <td>New York</td>
                <td>Traveling</td>
                <td>Whale</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Quinn Flynn
                    <input type="hidden" class="hobby" value="Swimming">
                    <input type="hidden" class="favorite-animal" value="Shark">
                </td>
                <td>Support Lead</td>
                <td>22</td>
                <td>2013/03/03</td>
                <td>$342,000</td>
                <td>Support</td>
                <td>Project K</td>
                <td>1</td>
                <td>Canada</td>
                <td>Vancouver</td>
                <td>Swimming</td>
                <td>Shark</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Charde Marshall
                    <input type="hidden" class="hobby" value="Writing">
                    <input type="hidden" class="favorite-animal" value="Wolf">
                </td>
                <td>Regional Director</td>
                <td>36</td>
                <td>2008/10/16</td>
                <td>$470,600</td>
                <td>Management</td>
                <td>Project L</td>
                <td>11</td>
                <td>Singapore</td>
                <td>Singapore</td>
                <td>Writing</td>
                <td>Wolf</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Haley Kennedy
                    <input type="hidden" class="hobby" value="Cycling">
                    <input type="hidden" class="favorite-animal" value="Bear">
                </td>
                <td>Senior Marketing Designer</td>
                <td>43</td>
                <td>2012/12/18</td>
                <td>$313,500</td>
                <td>Marketing</td>
                <td>Project M</td>
                <td>8</td>
                <td>UK</td>
                <td>Manchester</td>
                <td>Cycling</td>
                <td>Bear</td>
            </tr>
            <tr class="mainRecordRow table-light">
                <td></td>
                <td class="custom-cell-class">
                    Tatyana Fitzpatrick
                    <input type="hidden" class="hobby" value="Dancing">
                    <input type="hidden" class="favorite-animal" value="Peacock">
                </td>
                <td>Regional Director</td>
                <td>19</td>
                <td>2010/03/17</td>
                <td>$385,750</td>
                <td>Management</td>
                <td>Project N</td>
                <td>3</td>
                <td>India</td>
                <td>Mumbai</td>
                <td>Dancing</td>
                <td>Peacock</td>
            </tr>
        </tbody>
    </table>


    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/fixedheader/3.4.0/js/dataTables.fixedHeader.min.js"></script>
    <script src="https://cdn.datatables.net/rowgroup/1.4.0/js/dataTables.rowGroup.min.js"></script>

    <script>
    $(document).ready(function () {
        var groupColumn = 10;
        var initialColumnWidths = [];

        function storeOriginalClasses() {
            $('#example tbody tr').each(function () {
                $(this).data('original-classes', $(this).attr('class'));
                $(this).find('td').each(function () {
                    $(this).data('original-classes', $(this).attr('class'));
                });
            });
        }

        function reapplyOriginalClasses() {
            $('#example tbody tr').each(function () {
                var originalClasses = $(this).data('original-classes');
                if (originalClasses) {
                    $(this).attr('class', originalClasses);
                }
                $(this).find('td').each(function () {
                    var originalTdClasses = $(this).data('original-classes');
                    if (originalTdClasses) {
                        $(this).attr('class', originalTdClasses);
                    }
                });
            });

            $('.groupRow').each(function () {
                var originalClasses = $(this).data('original-classes');
                if (originalClasses) {
                    $(this).attr('class', originalClasses);
                }
            });
        }

        function addChildRows() {
            $('.child-row').remove();

            var colspan = $('#example thead tr th:visible').length - 1;

            table.rows().every(function (rowIdx, tableLoop, rowLoop) {
                var tr = $(this.node());
                var hobby = tr.find('.hobby').val();
                var favoriteAnimal = tr.find('.favorite-animal').val();

                var childRowHtml = `
                    <tr class="childDataRow table-light">
                        <td></td>
                        <td colspan="${colspan}">Hobby: ${hobby} | Favorite Animal: ${favoriteAnimal}</td>
                    </tr>
                `;

                tr.after(childRowHtml);
            });
        }

        function addGroupFooter() {
            $('.groupFooterRow').remove();

            var groupData = {};
            table.rows({ page: 'current' }).every(function () {
                var data = this.data();
                var groupKey = data[groupColumn];
                if (!groupData[groupKey]) {
                    groupData[groupKey] = {
                        totalSalary: 0,
                        totalYears: 0,
                        rows: []
                    };
                }
                groupData[groupKey].rows.push(this.node());
                groupData[groupKey].totalSalary += parseFloat(data[5].toString().replace(/[\$,]/g, ''));
                groupData[groupKey].totalYears += parseFloat(data[8]) || 0;
            });

            $.each(groupData, function (groupKey, groupInfo) {
                var lastRow = $(groupInfo.rows[groupInfo.rows.length - 1]);
                var lastChildRow = lastRow.next('.childDataRow').length ? lastRow.next('.childDataRow') : lastRow;

                var totalVisibleColumns = $('#example thead tr th:visible').length;
                var colspanBeforeSalary = 5;
                var colspanBetween = 2;
                var colspanAfterYears = totalVisibleColumns - 9;

                var formattedSalary = groupInfo.totalSalary.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
                var formattedYears = groupInfo.totalYears.toFixed(2);

                var groupFooterHtml = `
                    <tr class="groupFooterRow bg-primary text-white">
                        <td colspan="${colspanBeforeSalary}"></td>
                        <td>Group Total Salary: $${formattedSalary}</td>
                        <td colspan="${colspanBetween}"></td>
                        <td>Group Total Years: ${formattedYears}</td>
                        <td colspan="${colspanAfterYears}"></td>
                    </tr>
                `;

                lastChildRow.after(groupFooterHtml);
            });
        }

        // Capture initial column widths when the table is first drawn
        function captureInitialColumnWidths() {
            initialColumnWidths = [];
            $('#example thead th').each(function () {
                initialColumnWidths.push($(this).width());
            });
        }

        // Apply the captured column widths
        function applyColumnWidths() {
            $('#example thead th').each(function (index) {
                $(this).css('width', initialColumnWidths[index] + 'px');
            });

            $('#example tbody tr:first td').each(function (index) {
                $(this).css('width', initialColumnWidths[index] + 'px');
            });
        }

        storeOriginalClasses();

        var table = $('#example').DataTable({
            responsive: false,
            fixedHeader: true,
            scrollX: true,
            order: [[1, 'asc']],
            lengthChange: true,
            lengthMenu: [10, 25, 50, 100],
            paging: true,
            rowGroup: {
                dataSrc: groupColumn,
                startRender: function (rows, group) {
                    var colspan = $('#example thead tr th:visible').length;
                    return $('<tr/>')
                        .addClass('groupRow bg-primary-light text-white')
                        .append(`<td colspan="${colspan}">Group: ${group}</td>`);
                }
            },
            columnDefs: [
                {
                    targets: 0,
                    orderable: false,
                    searchable: false,
                    className: 'select-checkbox',
                    render: function () {
                        return '<input type="checkbox" class="select-row">';
                    }
                },
                {
                    targets: [10, 11, 12],
                    visible: false,
                    searchable: true
                },
                {
                    targets: 2,  // The "Position" column
                    width: '100px',  // Set width to 100px
                    createdCell: function (td, cellData, rowData, row, col) {
                        $(td).css('white-space', 'normal');  // Enable word wrapping
                        $(td).css('word-wrap', 'break-word');  // Ensure long words break
                    }
                }
            ],
            autoWidth: false
        });

        // Add child rows and group footers initially
        addChildRows();
        addGroupFooter();

        // Capture initial column widths after the table is drawn
        table.on('draw', function () {
            if (initialColumnWidths.length === 0) {
                captureInitialColumnWidths();
            }
            addChildRows();
            addGroupFooter();
            reapplyOriginalClasses();
            adjustTable();
        });

        // Apply fixed column widths during scrolling to avoid shifting
        $('#example_wrapper .dataTables_scrollBody').on('scroll', function () {
            applyColumnWidths();
        });

        // Adjust on column order change
        table.on('order', function () {
            adjustTable();
        });

        // Handle Select All checkbox
        $('#select-all').on('click', function () {
            var rows = table.rows({ 'search': 'applied' }).nodes();
            $('input[type="checkbox"]', rows).prop('checked', this.checked);
        });

        // Handle grouping change based on dropdown
        $('#group-by-column').on('change', function () {
            var selectedColumn = $(this).val();
            if (selectedColumn === "none") {
                groupColumn = null;
            } else if (selectedColumn === "position") {
                groupColumn = 2;
            } else if (selectedColumn === "office") {
                groupColumn = 10;
            }

            table.rowGroup().dataSrc(groupColumn).draw();
            adjustTable();
        });

        // Function to adjust columns and fixed header alignment
        function adjustTable() {
            table.columns.adjust();
            if (table.fixedHeader) {
                table.fixedHeader.adjust();
            }
        }

        // Fix header alignment on window resize
        $(window).on('resize', function () {
            adjustTable();
        });

        // Recalculate column width on table draw events, like search or paging
        table.on('responsive-resize', function () {
            adjustTable();
        });

        // Call the adjustment function on initial load to ensure correct alignment
        adjustTable();
    });
    </script>
</body>
</html>



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video