fixedHeader and scrollX

fixedHeader and scrollX

Bolt1Bolt1 Posts: 3Questions: 2Answers: 0

I have seen many posts saying scrollX and fixedHeader do not play well together. However, I have seen a few examples (such as Horizontal page scrolling) where they do work as desired, the header will scroll horizontally with the table. I have made numerous attempts to duplicate the action in my own tables, with no luck.

This is what I have:

$(document).ready(function() {
    $('#table').DataTable({
        "order": [[3, "desc" ]],
        searching: false,
        paging: false,
        fixedHeader: {
            header: true,
            headerOffset: 45,
            },
        scrollX: true
    });
});
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/fixedheader/3.1.3/css/fixedHeader.dataTables.min.css">
<script src="https://code.jquery.com/jquery-1.12.4.js" type="text/javascript"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" type="text/javascript"></script>
<script src="https://cdn.datatables.net/fixedheader/3.1.3/js/dataTables.fixedHeader.min.js" type="text/javascript"></script>

What am I overlooking?

«1

Answers

  • kthorngrenkthorngren Posts: 21,341Questions: 26Answers: 4,954
    edited January 2018

    The FixedHeader docs state this:

    Please note that FixedHeader is not currently compatible with tables that have the scrolling features of DataTables enabled (scrollX / scrollY). Please refer to the compatibility table for full compatibility details.

    The example you mentioned does not use ScrollX but simply the browsers scrolling capabilities.

    It is noted in the compatibility matrix that FixedHeader and DT scrolling features compatibility will be resolved in a future release.

    You are not overlooking anything... they just don't work well together currently.

    Kevin

  • lramondevlramondev Posts: 1Questions: 0Answers: 0

    Remove "appendTo("body")" in the file dataTables.fixedHeader.min.js

  • allanallan Posts: 63,516Questions: 1Answers: 10,472 Site admin

    Its not as simple as that I'm afraid. FixedHeader assumes that there is no scrolling wrapping div for the header, but of course when scrolling is enabled, there is.

    There is no workaround for this at the moment - that functionality would need to be written.

    Allan

  • sidewalksidewalk Posts: 2Questions: 0Answers: 0

    Well there is a "fix" for that:

    JS:
    $('.dataTables_scrollBody').scroll(function(){
    $('.fixedHeader-floating').scrollLeft($(this).scrollLeft());
    });

    CSS:
    table.datatable.dataTable.no-footer.fixedHeader-floating {
    top: 0px;
    width: 100% !important;
    display: block;
    overflow-x: auto;
    }

    This will only work if added via web inspector (developer tools) since the fixedHeader-floating table is added to DOM dynamically (via appendTo("body") as Iramondev pointed). Perhaps using stylesheet (display: none/block) on .fixedHeader-floating with the code above could fully solve the issue

  • sidewalksidewalk Posts: 2Questions: 0Answers: 0

    Ignore my previous post. Here is the working solution:

    Add this to your stylesheet:

    table.dataTable.fixedHeader-floating {
         display: none !important; /* Hide the fixedHeader since we dont need it*/
    }
    
    
    .dataTables_scrollHeadInner{
        margin-left: 0px;
        width: 100% !important;
        position: fixed;
        display: block;
        overflow: hidden;
        margin-right: 30px;
        background: white;
        z-index: 1000;
    }
    
    .dataTables_scrollBody{
        padding-top: 60px;
    }
    

    And this to your datatable declaration:

    $('.datatable').DataTable({
        scrollX: true,
        fixedHeader: true,
        "initComplete": function(settings, json){
    
                    $('.dataTables_scrollBody').on('scroll',function(){
                        $('.dataTables_scrollHeadInner').scrollLeft($(this).scrollLeft());
                    });
    
                    $(document).on('scroll',function(){
    
                        var scroll_pos = $(this).scrollTop();
                        var margin = 74; // Adjust it to your needs
                        var cur_pos = $('.dataTables_scrollHeadInner').position();
                        var header_pos = cur_pos.top;
    
                        if(scroll_pos < margin)
                             var header_pos = margin - scroll_pos;
                        else
                            header_pos = 0;
    
                        $('.dataTables_scrollHeadInner').css({"top" : header_pos});
                    });
                }
    })
    
  • Mert1296Mert1296 Posts: 43Questions: 7Answers: 0

    @sidewalk

    In your DataTable declaration on line 4:
    I don't understand why there is json in your Parameter..
    Can you explain why I Need this?

    Kind regards

  • kthorngrenkthorngren Posts: 21,341Questions: 26Answers: 4,954

    In your DataTable declaration on line 4:
    I don't understand why there is json in your Parameter..
    Can you explain why I Need this?

    The initComplete docs explain the usage of the json parameter.

    Kevin

  • Mert1296Mert1296 Posts: 43Questions: 7Answers: 0

    Hmm.. Is that useful when I don't work with a JSON-File?

  • kthorngrenkthorngren Posts: 21,341Questions: 26Answers: 4,954

    Hmm.. Is that useful when I don't work with a JSON-File?

    If you don't need the parameter you don't need to pass it into the function. This is off topic from the thread. If you have more questions about initCompete please start a new thread topic.

    Kevin

  • mewaligmewalig Posts: 5Questions: 1Answers: 0
    edited March 2019

    The below few lines of javascript should work (without requiring css changes), if your table is inside a div container:

      let xscroller = container.querySelectorAll('.dataTables_scrollBody')[0];
      let head = container.querySelectorAll('.dataTables_scrollHead')[0];
      xscroller.addEventListener('scroll', function(e) {
        head['style'].left = '-' + e.target['scrollLeft'] + 'px';
      });
    
  • mewaligmewalig Posts: 5Questions: 1Answers: 0

    Forgot one line to change overflow

      let xscroller = container.querySelectorAll('.dataTables_scrollBody')[0];
      let head = container.querySelectorAll('.dataTables_scrollHead')[0];
      head['style'].overflow = ''; // <<<
      xscroller.addEventListener('scroll', function(e) {
        head['style'].left = '-' + e.target['scrollLeft'] + 'px';
      });
    
  • int14int14 Posts: 8Questions: 3Answers: 0

    Hi @sidewalk, I am able to fix the header using your solution but when I am checking the responsive the header is not visible.

    Any suggestion...

  • jpsiyyadrijpsiyyadri Posts: 1Questions: 0Answers: 0

    Try setting scrollY to some height and try - it works as a normal table scroll y behaviour

  • FelipeGualbertoFelipeGualberto Posts: 1Questions: 0Answers: 0

    mewalig's solution worked for me. It even works with fixedColumns.

  • hwijayahwijaya Posts: 4Questions: 0Answers: 0

    I have read for 2 days about this, so joining all of them together, here's my contribution.

    I got it figured out, hopefully this is useful for someone or help in the development as well.

    My datatables is in a DIV and horizontal Scrolling enable due to huge table. When fixed header was set it was set as FIXED, and a new table is inserted at the BODY rather than inside the div.

    I made it appended to the DIV instead of BODY so that the overflow rule might be inherited.

    File:
    dataTables.fixedHeader.min.js (search for "appendTo")
    From:

    e.table().node().cloneNode(!1)).removeAttr("id").append(f).appendTo("body")
    

    To:

    e.table().node().cloneNode(!1)).removeAttr("id").append(f).appendTo(".dataTables_scroll")
    

    Now that it's appended to the the datatables-created-div, same level as dataTables_scrollHead, dataTables_scrollBody rather than stranded alone at body, whatever overflow still showing/sticking out.

    File:
    fixedHeader.bootstrap.min.css
    From:

    table.dataTable.fixedHeader-floating{position:fixed !important}
    

    To

    table.dataTable.fixedHeader-floating{position:absolute !important}
    

    or File:
    fixedHeader.dataTables.min.css
    From:

    table.fixedHeader-floating{position:fixed !important;background-color:white;}
    

    To

    table.fixedHeader-floating{position:absolute !important;background-color:white;}
    

    Careful of CACHE of the CSS and JS files.

    Now that the floating sticky row has appeared but out of place and overflow in effect.
    Have this JS running, detecting when fixedHeader-floating appears, keep adjusting them to follow the horizontal scroll and stick to the top.

    setInterval(function(){
      if($('.fixedHeader-floating').is(':visible')){
         var myoffset = Math.round($(window).scrollTop() - $('#Detail2Container').position().top + $('.topbar').height() - 145);
         var positionleft = $('.dataTables_scrollHeadInner').position();
         $('.fixedHeader-floating').css({ 'top': myoffset, 'left': positionleft.left + 10 });
      }
    }, 50); //every 50ms
    

    Detail2Container is the DIV that wrap the Datatables.
    I couldn't use dataTables_wrapper as reference as there are a few of them in the same page. In my page, I only one table that needs fixedHeader, if I need 2, it will be tough. But I will deal with it when the needs arise.

    You could adjust the calculation according to your own design.

    2 days for me to figure this out. So I feel like sharing it too.

  • allanallan Posts: 63,516Questions: 1Answers: 10,472 Site admin

    Thank you for posting this and taking the time to research this!

    We've never taken this further ourselves as FixedHeader and scrollY are effectively two different ways of achieving the same thing (keeping the headers visible). And we'd presumed that if you were going to use scrollX then you would also look at using scrollY. But I guess we were wrong in that assumption!

    Regards,
    Allan

  • paskuale75paskuale75 Posts: 5Questions: 0Answers: 0

    so there is no way to make the two live together?

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Not officially, but solutions like the one discussed in this thread may be able to help,

    Colin

  • rattybagrattybag Posts: 1Questions: 0Answers: 0

    I have found a way to achieve this functionality with less code than what the above replies have...

    The block element (which is your DataTables container) will need to have overflow-x: scroll placed on it. How this works is that when the user scrolls inside the container that the DataTable is rendered in, the header is then scrolled alongside using left positioning.

    $('.block').scroll(function() { var scrollAmt = $(this).scrollLeft(); $('.fixedHeader-floating').css('left', 0 - parseInt(scrollAmt) + 'px'); });
    
  • martoomartoo Posts: 5Questions: 1Answers: 0
    edited December 2020

    This one is a real headache. I could not make any of the above examples work.
    (my configuration is mvc5, datatables, bootstrap 4)
    I found that this is the simplest:

    $(".dataTable").css("overflow", "unset");

    The problem is that when you go right (when you have a lot of columns) the header column in the invisible half is transparent and I cannot find a way to change this.

    Edit:

    table.dataTable thead tr {
    background-color: white;
    }

  • allanallan Posts: 63,516Questions: 1Answers: 10,472 Site admin

    Have you got a link to an example showing that? I should be able to see what CSS selector would be required to target that cell specifically.

    Allan

  • martoomartoo Posts: 5Questions: 1Answers: 0

    Hey Allan,

    I thought so to - find the selector, F12, no problems. Could not make it!

    It's a private software which requires credentials... Fixed it with settings the color explicitly to white. If you like to see it we can do it over skype or something like that.

  • Skylord123Skylord123 Posts: 3Questions: 0Answers: 0

    I took the fix from @rattybag and modified it to work better for my needs.

    I have several tables on one page (with tabs to chose between them) so I needed a solution that worked with multiple tables.

    My tables are also within a container that has a margin. This margin was causing the table and header to be out of sync with each other. I modified the code so it gets how far away the table is from the edge of the screen and factors that into the scroll.

        $(".dataTables_scrollBody").scroll(function() {
            $('.fixedHeader-floating').css('left', this.getBoundingClientRect().x - parseInt($(this).scrollLeft()) + 'px');
        });
    

    I use this with both fixedHeader and scrollX set to true.

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Nice, thanks for reporting back,

    Colin

  • Skylord123Skylord123 Posts: 3Questions: 0Answers: 0

    No problem, sharing is caring!

    I wanted to build on what I did the other day. I ran into the issue where my code from my previous comment had to be executed after the tables had initialized. I updated it so it now listens for the preInit.dt event and only hooks into the table if the table has fixedHeader and scrollX set to true. This also uses the datatable API to get the elements which in theory should mean it's faster as well (instead of having to search through the dom).

    $(function(){
        // fix to get fixedHeader and scrollX working together in datatables
        $(document).on( 'preInit.dt', function (e, settings) {
            if(
                settings.oInit
                && settings.oInit.fixedHeader
                && settings.oInit.scrollX
            ) {
                $(settings.nScrollBody).scroll(function() {
                    if(!settings._fixedHeader) {
                        return false;
                    }
    
                    let tableFloatingHeader = settings._fixedHeader.dom.header.floating;
                    if(tableFloatingHeader) {
                        tableFloatingHeader.css('left', this.getBoundingClientRect().x - parseInt($(this).scrollLeft()) + 'px');
                    }
                });
            }
        });
    });
    
  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Nice!

  • kottankottan Posts: 1Questions: 0Answers: 0

    @Skylord123: Thanks for sharing your code!
    I noticed that the header isnt in the correct position, if i first scroll to the right and THEN scroll down. Would be nice to know if it works in your datatable, or if something is wrong with mine.

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    @kottan We're happy to take a look, but as per the forum rules, please link to a test case - a test case that replicates the issue will ensure you'll get a quick and accurate response. Information on how to create a test case (if you aren't able to link to the page you are working on) is available here.

    Cheers,

    Colin

  • yasirunanayakkarayasirunanayakkara Posts: 2Questions: 0Answers: 0

    @sidewalk

    Thank you buddy

This discussion has been closed.