DataTables / Responsive

Responsive extension for DataTables, providing support for complex tables on all device screen sizes
Other
148 stars 86 forks source link

Multiple header rows #71

Closed Petah closed 9 months ago

Petah commented 8 years ago

Is there a way to get this to work with multiple header rows? If not is there any event we can bind to when a column display is toggled?

DataTables commented 8 years ago

I had thought there was already an issue for this but I don't actually see it. Responsive 2.0 does not currently support multiple headers / footers. That is something I plan to resolve for 2.1, although I'm not sure what the time frame for that is yet.

Regarding the event, you could listen for the responsive-resize event (which isn't yet in the release version - you'll need to use the code from this repo or the nightly version). It will be in 2.0.1.

ohansrud commented 8 years ago

I encountered the same issue in my project, and modified the _resize() method to check for multiple headers. Check out my fork at https://github.com/ohansrud/Responsive.

The modified function uses JQuery and bootstrap to hide and show the header th's. Not sure if this an optimal solution or if this can be solved without those libraries.

leveneg commented 8 years ago

I do something like this sometimes. It's not 100% responsive, but hopefully it gets you in the right direction.

zoobe50 commented 7 years ago

Hi @DataTables , I am using * angular-datatables - v0.5.5 and Responsive 2.0.2 plug-in. I need to implement 2 rows in header (i.e is Column Name, and Filter), responsive works fine when I use 1 header row (Column Name only) but fails to deliver valid responsive layout when I try to use 2 header rows. Please advise.

COLUMN1 COLUMN2 COLUMN3 COLUMN4 COLUMN5
FILTER1 FILTER2 FILTER3 FILTER4 FILTER5
ROW 1 ROW 1 ROW 1 ROW 1 ROW 1
ROW 2 ROW 2 ROW 2 ROW 2 ROW 2
DataTables commented 7 years ago

Currently it isn't possible due to this on going enhancement request.

brunobg commented 7 years ago

A fix for this would be nice. If you can give me a pointer to what code should be changed I can try to send a PR.

For those of you looking for a workaround, here's a straight jquery version similar to @leveneg proposed:

    var $table = $('table.datatable');
    $table.dataTable(params);

    // workaround for https://github.com/DataTables/Responsive/issues/71
    $table.on('responsive-resize.dt', function(e, datatable, columns) {
        for (var i in columns) {
            var index = parseInt(i, 10) + 1;
            $table.find('th:nth-child(' + index + ')').toggle(columns[i]);
        }
    });
DataTables commented 7 years ago

For the sample case of multiple rows with the same number of cells in each, its probably not too bad - that workaround just needs to be embedded into the code here (more or less...).

Where it will get really difficult is when considering colspan / rowspan.

FrancYescO commented 7 years ago

another limitation of @brunobg workaround is that seems it get buggy (hiding wrong header column) if we have some already hidden columns ("visible": false)

brunobg commented 7 years ago

@FrancYescO can you explain how the bug might happen? The columns parameter comes from datatables itself, so my workaround makes sure the header columns reflect that.

FrancYescO commented 7 years ago

I have a table with first 2 column (containing an id and a checkbox) that usually i hide with aocolumndef > visible: false ... Second row used for filters

Seems in this manner your code is just calculating the wrong header column to hide resulting in having the header shifted respect data

I'm from mobile... I'll try to give you a jsfiddle if hard to get it

FrancYescO commented 7 years ago

http://jsfiddle.net/UvjnT/1463/

try to get the space for the table smaller and you will see the "Platform(s)" over the data that instead are the browsers

leveneg commented 7 years ago

@FrancYescO try using the 'column-visibility.dt' event. It gets the column and the column visibility state passed to the callback.

oortega commented 7 years ago

@FrancYescO Did you find the solution?, i have the same issue with columns hides. Thank you

oortega commented 7 years ago

I really needed to put search fields in the header, then I put the fields in the tfoot and then tfoot I put down thead, that's enough for me.

First https://datatables.net/examples/api/multi_filter.html
Then `table.dataTable tfoot {    display: table-header-group;}`
FrancYescO commented 7 years ago

Unfortunately not, still haven't found a way to get double header 100% responsive

ghost commented 7 years ago

I have 2 rows in my header and the first it' who have merged some columns using colspan. thanks to @ohansrud for his workaround i can create a little better behaviour, i just replace in the master code the function _setColumnVis by this:

    _setColumnVis: function ( col, showHide )
    {
        var dt = this.s.dt;
        var display = showHide ? '' : 'none'; // empty string will remove the attr

        $( dt.column( col ).header() ).css( 'display', display );
        $( dt.column( col ).footer() ).css( 'display', display );
        dt.column( col ).nodes().to$().css( 'display', display );

        var colspanLength = 0;
                var filtersLength = 0;
                var header = dt.header();
                if (header[0].children.length > 1) {
            var filters = header[0].children[0].children;
                for (var i = 0; i < filters.length; i++) {
                    if($(filters[i]).css('display') != 'none') {
                            filtersLength ++;
                            var colspan = $(filters[i]).attr('colspan');
                            if(parseInt(colspan)) {
                                colspanLength += parseInt(colspan);
                            } else {
                                colspanLength += 1;
                            }
                    }
                }
                if(!showHide && (col < colspanLength)) {
                        if(filtersLength < 2)
                            filtersLength = 1;
                        $(filters[filtersLength - 1]).css('display', display);
                } else if(showHide && (col > colspanLength)) {
                    $(filters[filtersLength]).css('display', display);
                }
        }
    },

In certainly cases won't work good, but for others it's just working. No idea if this could help.

Jaded-Design commented 7 years ago

So what changed between v1 and v2 that broke this functionality? We are in the process of trying to upgrade some applications to v2 and now all of our tables that used rowspan/colspan are breaking when tables are put in to responsive mode. This worked fine in v1 so I'm wondering what changed and if I can update v2 with similar code or if I just need to stay on v1 until v2 is fixed.

DataTables commented 7 years ago

The problem is that v1 used DataTables built in column visibility API. Since that has support for complex headers built in we basically got that in Responsive for free. However it caused a lot of confusion and issues since developers also wanted to be able to control the column visibility via Buttons, or directly via the API.

So v2 implements its own column hiding (using display:none). That doesn't yet have support for complex headers. Actually, more generally I want to expose DataTables' APIs for this, which I will be doing in the next major version of DataTables, and which is why I've not progressed this yet.

Jaded-Design commented 7 years ago

So it looks like if I run responsive 1.0.7 with Datatables 1.10.13 that everything works fine. Are there any gotchas to using these two versions together that I may be missing?

DataTables commented 7 years ago

I've not run to the two together myself so I can't say for sure, but I can't think of anything off the top of my head.

FadiNouh1 commented 7 years ago

I found better way to insert header using angular-jquery I made solution for the tables which not created in angular way - or the tables which creating columns dynamically I made function that create the extra header for my table

function buildTableHeader (headerData) {
          var startTr = '<tr class="top-tb-header">';
          var endTr = '</tr>';
          var postitionHeader = '<th colspan="7">New Header</th>';
          var fullHeader = startTr + postitionHeader +  endTr;
          return fullHeader;
  }

I called this function after drawing the table callback as follwoing>>

  var dtOfferOptions = DTOptionsBuilder.fromFnPromise(function() {return $q.when(vm.Data)})
              .withOption('drawCallback', function(settings) {
                var topTableHeader = buildTableHeader(supplierData);
                angular.element(document).find('.dataTable .top-tb-header').remove(); //this remove the header when the callback is running when sorting for exmple
                angular.element(document).find('.dataTable thead').prepend(topTableHeader); // put the header it in the top of the table 
              })
niravzaveri commented 6 years ago

I would love to see a solution here. I am having extra row that I render after datatable for my customized filters for each column.

FrancYescO commented 6 years ago

2nd birthday for this issue

DataTables commented 6 years ago

I'm currently working on other aspects of DataTables. When I've had time to resolve this issue, I will update the thread here.

amaurysalgado commented 6 years ago

I have the same problem when using the options 'responsive: true', to remove those icons I only override the actual css, for this one.

.dataTables_scrollBody thead .sorting_desc::after{ content: "" !important; } .dataTables_scrollBody thead .sorting_asc::after{ content: "" !important; } .dataTables_scrollBody thead .sorting::after{ content: "" !important; }

Anaelle commented 6 years ago

Hi, I struggled with the two headers responsive problem for two days now. Here is the solution I propose (I overrided the function into my own JS): Adding a small code part at the end of the _resizeAuto function to manage the second header minimal width. The better solution is to go through every tr into the thead tag, but in my case I only have to manage the responsive with two trs.

Hope this helps!

_resizeAuto : function(){
        var dt = this.s.dt;
        var columns = this.s.columns;

        // Are we allowed to do auto sizing?
        if ( ! this.c.auto ) {
            return;
        }

        // Are there any columns that actually need auto-sizing, or do they all
        // have classes defined
        if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
            return;
        }

        // Need to restore all children. They will be reinstated by a re-render
        if ( ! $.isEmptyObject( _childNodeStore ) ) {
            $.each( _childNodeStore, function ( key ) {
                var idx = key.split('-');

                _childNodesRestore( dt, idx[0]*1, idx[1]*1 );
            } );
        }

        // Clone the table with the current data in it
        var tableWidth   = dt.table().node().offsetWidth;
        var columnWidths = dt.columns;
        var clonedTable  = dt.table().node().cloneNode( false );
        var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
        var clonedBody   = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8

        // Header
        var headerCells = dt.columns()
            .header()
            .filter( function (idx) {
                return dt.column(idx).visible();
            } )
            .to$()
            .clone( false )
            .css( 'display', 'table-cell' );

        // Body rows - we don't need to take account of DataTables' column
        // visibility since we implement our own here (hence the `display` set)
        $(clonedBody)
            .append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
            .find( 'th, td' ).css( 'display', '' );

        // Footer
        var footer = dt.table().footer();
        if ( footer ) {
            var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
            var footerCells = dt.columns()
                .footer()
                .filter( function (idx) {
                    return dt.column(idx).visible();
                } )
                .to$()
                .clone( false )
                .css( 'display', 'table-cell' );

            $('<tr/>')
                .append( footerCells )
                .appendTo( clonedFooter );
        }

        $('<tr/>')
            .append( headerCells )
            .appendTo( clonedHeader );

        // In the inline case extra padding is applied to the first column to
        // give space for the show / hide icon. We need to use this in the
        // calculation
        if ( this.c.details.type === 'inline' ) {
            $(clonedTable).addClass( 'dtr-inline collapsed' );
        }

        // It is unsafe to insert elements with the same name into the DOM
        // multiple times. For example, cloning and inserting a checked radio
        // clears the chcecked state of the original radio.
        $( clonedTable ).find( '[name]' ).removeAttr( 'name' );

        var inserted = $('<div/>')
            .css( {
                width: 1,
                height: 1,
                overflow: 'hidden',
                clear: 'both'
            } )
            .append( clonedTable );

        inserted.insertBefore( dt.table().node() );

        // The cloned header now contains the smallest that each column can be
        headerCells.each( function (i) {
            var idx = dt.column.index( 'fromVisible', i );
            columns[ idx ].minWidth =  this.offsetWidth || 0;
        } );

        // I ADDED THIS PART TO MANAGE THE SECOND HEADER WIDTH
        var headerCells2 = $(dt.table().header()).find('tr:eq(1) th');
        headerCells2.each( function (i) {
            var idx = dt.column.index( 'fromVisible', i );

            if (columns[ idx ].minWidth < this.offsetWidth) {
                columns[ idx ].minWidth = this.offsetWidth;
            }
        } );

        inserted.remove();
    }
FrancYescO commented 6 years ago

i think this is the best solution i've found, will work if you have the same number of columns in the header subrows:

            table.on('responsive-resize.dt', function(e, datatable, columns) {
                columns.forEach(function(is_visible, index) {
                    $.each($('tr', datatable.table().header()), function() {
                        var col = $($(this).children()[index]);
                        is_visible == true ? col.show() : col.hide();
                    });
                });
            });

thanks to https://github.com/cristoper/datatables-filterwidgets/commit/8fd638646814109273880d3c491a37a6b53fe96e for the idea

danybimbo commented 6 years ago

Thanks FrancYescO. It solved my problem with columnFilter

chilek commented 5 years ago

Other problem related to multiple header rows is that although responsive-resize event handler helps to toggle visibility of custom header cells it doesn't trigger (this event) after page load. So this way we have visual broken table until first responsive-resize event. Is there a way to workaround this problem?

DataTables commented 5 years ago

Currently no. That is something that needs to be address along side proper support for multi-row headers.

chilek commented 5 years ago

:cry: Maybe some donation could help?

DataTables commented 5 years ago

I don't realistically see this being done before 2019. I've implemented new header support in DataTables 2 (which is still on going dev work) and that will form the foundation for improved header support in Responsive.

chilek commented 5 years ago

@DataTables will dt2 have div:flexbox table support?

DataTables commented 5 years ago

I'm not sure what you mean by that? Could you show me an example?

chilek commented 5 years ago

Sorry for not clear comment: https://css-tricks.com/snippets/css/a-guide-to-flexbox/

DataTables commented 5 years ago

Sorry I wasn't clear either. I know what flexbox is, but I don't know what you mean by "div:flexbox table support". flexbox isn't used for table layout.

chilek commented 5 years ago

I meant DT which works using div:flexbox and dynamically adjust row collapsing to column. Would be great if it used some column weights/priorities to determine collapsing order.

DataTables commented 5 years ago

You mean using <div> elements instead of a <table>? No - that is not something that I currently plan.

Would be great if it used some column weights/priorities to determine collapsing order.

That is already possible: https://datatables.net/extensions/responsive/priority .

chilek commented 5 years ago

Is it able to move cells from parent column to child row?

DataTables commented 5 years ago

<td> cells, no, since the child row might or might not contain its own table. But it can move the child nodes of a cell.

chilek commented 5 years ago

I wish it worked for tables based on div flexbox layout.

chilek commented 5 years ago

@DataTables returning to:

Other problem related to multiple header rows is that although responsive-resize event handler helps to > toggle visibility of custom header cells it doesn't trigger (this event) after page load. So this way we > have visual broken table until first responsive-resize event. Is there a way to workaround this problem?

Would it be able to add reponsive-resize event trigger just a moment after initial datatables display? It would resolve the problem with broken layout after page load. Would it require redesign all datatables and responsive extension code?

DataTables commented 5 years ago

It already does trigger that event, you just need to listen for it before initialising the DataTable:

$('#myTable')
  .on( 'responsive-resize.dt' )
  .DataTable( ... );
chilek commented 5 years ago

As first responsive-resize.dt I get for columns parameter: [true, "-", "-", "-", "-", "-", "-", "-", "-"] For further event I get columns parameter with value: [true, true, true, true, true, true, true, true, false] I think columns parameter for first trigger is useless as I don't get true column visibilty states :( Am I wrong or don't I know something? Maybe function prototype is different for first event?

DataTables commented 5 years ago

If you could open a thread on the forum with a test case showing the issue that would be great. Thanks.

chilek commented 5 years ago

Forum post reference: https://datatables.net/forums/discussion/52391/responsive-resize-dt-problem

AllanJard commented 9 months ago

Multi-row headers and footers and colspan / rowspan in header / footer support was recently added and will ship as Responsive 3 alongside DataTables 2.