1. Fabrik 3.9 has been released. If you have updated Joomla to 3.9, this is a required update.
    Dismiss Notice

Fixed table headers

Discussion in 'Community' started by Bauer, Nov 3, 2014.

  1. Bauer

    Bauer Well-Known Member

    Level: Supporter
    If anyone wants to have fixed table headers in a list (i.e list column headers always visible and fixed at top of window when scrolling down) - here's some javascript/jquery code to do the trick. (Only tested with the default bootstrap list template).

    Update note: See Post #33 in this thread. I updated the script to work with tables that have included embedded filters in the table header, toggle column display, fixed table footer, and/or an existing fixed menu bar.

    Replace all instances of TABLEID in this code with the id of the html table that is used by the fabrik list being displayed (e.g. use firebug to cut/paste the table id exactly as shown in the <table> tag of your form).

    Then just put this code in the list_##.js file for the list you want to use it in.

    Code (Text):
    jQuery(document).ready(function(){
        window.headerHeight = jQuery("table#TABLEID thead").innerHeight();
        window.eTop = jQuery("table#TABLEID tbody").offset().top;
        window.tableWidth = jQuery("table#TABLEID").width();
        jQuery(window).scroll(function(event){
            window.bodyTop = jQuery(this).scrollTop();
            window.thisTop = window.eTop - jQuery(window).scrollTop(); //position of the ele window
            // Use for debugging
            //console.log("headerHeight::"+window.headerHeight+" | bodyTop::" + window.bodyTop + " | eTop::"+window.eTop+" | thisTop::"+thisTop);
            if (window.thisTop < window.headerHeight ) {
                jQuery("table#TABLEID thead").css({"position":"fixed","top":"0px","z-index":"9999"});
                var k = 0;
                jQuery("table#TABLEID").width(window.tableWidth);
                jQuery("table#TABLEID thead tr th").each(function ()
                {
                    jQuery(this).width(window.hcol[k]);
                    k++;
                });
            }else{
                jQuery("table#TABLEID thead").css({"position":"relative","top":"0px","z-index":"9999"});
            }
        });
     
        /* resets header on window resize */
        jQuery(window).on('resize', function(){
            jQuery("table#TABLEID thead").css({"position":"relative","top":"0px","z-index":"9999"});
            setHeaders();
        });
     
        setHeaders();
     
        /* (Optional) This scrolls the table header to the top on load  */
        jQuery(this).scrollTop(window.eTop - window.headerHeight);
    });
     
    function setHeaders() {
        // initialize table header column widths
        window.hcol = new Array();
        var xwidth = 0;
        var k = 0;
        jQuery("table#TABLEID thead tr th").each(function (){
            xwidth = jQuery(this).width();
            window.hcol[k] = xwidth;
            k++;
        });
    }
    You will also want to add some css for this 'fixed' thead element, to spruce it up a bit. (It is transparent otherwise). It seemed to affect operations when added via jQuery - so I just put it in the css stylesheet for the list template. e.g....

    table#TABLEID thead {
    background-color:#ffffff;
    }

    With a little more work this could be incorporated as an option in the fabrik list model. (hint hint)
     
  2. d.bergonzi

    d.bergonzi Member

    Level: Community
    Sorry ... maybe my little experience.

    I saw your post and I think it will be useful, but can not figure out how and where to implement the script indicated.

    Can you give me some more information?

    Thank You!
     
  3. Bauer

    Bauer Well-Known Member

    Level: Supporter
  4. cheesegrits

    cheesegrits Support Gopher Staff Member

    Level: Community
    This would might be useful in the wiki somewhere ...

    -- hugh
     
  5. d.bergonzi

    d.bergonzi Member

    Level: Community
    good solution.
    I have to work a bit more because I could not but I'm sure is the right way. I hope to do it! ;)
     
  6. Bauer

    Bauer Well-Known Member

    Level: Supporter
    Assuming you are using a browser debugging tool such as firebug - try taking out the comments in the line I have marked as 'Use for debugging' and see if you get results in your console.log. If you can't get it to work, post your javascript code and maybe I'll be able to identify why it's not working.
     
  7. d.bergonzi

    d.bergonzi Member

    Level: Community
    Hi...sorry for delay:confused: .....problem still found..then.......follow the code that i have insert in file "list_36.js" where 36 is ID of my fabrik table anagrafica_noleggiatori

    I have tried with different filename ... tentative solution :)
    e.g.
    anagrafica_noleggiatori_36.js


    Code (Text):

    jQuery(window).load( function() {
        window.headerHeight = jQuery("table#anagrafica_noleggiatori thead").innerHeight();
        window.eTop = jQuery("table#anagrafica_noleggiatori tbody").offset().top;
        window.tableWidth = jQuery("table#anagrafica_noleggiatori").width();
        jQuery(window).scroll(function(event){
            window.bodyTop = jQuery(this).scrollTop();
            window.thisTop = window.eTop - jQuery(window).scrollTop(); //position of the ele window
            // Use for debugging
            //console.log("headerHeight::"+window.headerHeight+" | bodyTop::" + window.bodyTop + " | eTop::"+window.eTop+" | thisTop::"+thisTop);
            if (window.thisTop < window.headerHeight ) {
                jQuery("table#anagrafica_noleggiatori thead").css({"position":"fixed","top":"0px"});
                var k = 0;
                jQuery("table#anagrafica_noleggiatori").width(window.tableWidth);
                jQuery("table#anagrafica_noleggiatori thead tr th").each(function ()
                {
                    jQuery(this).width(window.hcol[k]);
                    k++;
                });
            }else{
                jQuery("table#anagrafica_noleggiatori thead").css({"position":"relative","top":"0px"});
            }
        });
     
        /* resets header on window resize */
        jQuery(window).on('resize', function(){
            jQuery("table#anagrafica_noleggiatori thead").css({"position":"relative","top":"0px"});
            setHeaders();
        });
     
        setHeaders();
     
        /* (Optional) This scrolls the table header to the top on load  */
        jQuery(this).scrollTop(window.eTop - window.headerHeight);
    });
     
    function setHeaders() {
        // initialize table header column widths
        window.hcol = new Array();
        var xwidth = 0;
        var k = 0;
        jQuery("table#anagrafica_noleggiatori thead tr th").each(function (){
            xwidth = jQuery(this).width();
            window.hcol[k] = xwidth;
            k++;
        });
    }
     
     
  8. Bauer

    Bauer Well-Known Member

    Level: Supporter
    I have attached a screen snip of the html of a fabrik table as shown in firefox debug.
    The line that contains the id is in blue.

    I cut and pasted my sample code above just to test in a different list_##.js file (as I posted this months ago).
    I then used serach and replace to replace all occurences of 'TABLEID' with 'list_117_com_fabrik_117' per my initial instructions.

    At first it seemed to "not work" for me either.:oops: Then I realized I am now using a Main Menu module bar at the top of the page which also lockes into a fixed position at the top when scrolled. So the fixed header from the fabrik form was there - but it was hidden 'behind' the Main Menu bar. By simple adding a z-index value to the css style via my javascript code, the fixed menu appeared as expected - 'on top of' my Main Menu.

    I am guessing this was your problem too.
    ALSO: Note that TABLEID is not simply replaced with the fabrik list id - but the EXACT html <table> tag id that is used by your list as seen when you examine the page source. (see my screen snip)

    That javascript code should work on any page with a table that has an id in the <table> tag and uses a <thead> tag for the headers - fabrik or otherwise.

    Please note that I have changed the example code in the original post above to include "z-index:9999" in the style tag. (If that still doesn't work change the z-index value to a higher number.)

    Also to be consitent with my other fabrik js files, I changed the first line from...
    jQuery(window).load( function() {
    to...
    jQuery(document).ready(function(){
     

    Attached Files:

  9. d.bergonzi

    d.bergonzi Member

    Level: Community
    ....i have founded in firebug the id table.....(se attach).....

    ....then.....code in "/components/com_fabrik/jsanagrafica_noleggiatori_36.js"........but.....:(.....nothing.......where am I wrong?

    Code (Text):
    jQuery(document).ready(function(){
        window.headerHeight = jQuery("table#list_36_com_fabrik_36 thead").innerHeight();
        window.eTop = jQuery("table#list_36_com_fabrik_36 tbody").offset().top;
        window.tableWidth = jQuery("table#list_36_com_fabrik_36").width();
        jQuery(window).scroll(function(event){
            window.bodyTop = jQuery(this).scrollTop();
            window.thisTop = window.eTop - jQuery(window).scrollTop(); //position of the ele window
            // Use for debugging
            //console.log("headerHeight::"+window.headerHeight+" | bodyTop::" + window.bodyTop + " | eTop::"+window.eTop+" | thisTop::"+thisTop);
            if (window.thisTop < window.headerHeight ) {
                jQuery("table#list_36_com_fabrik_36 thead").css({"position":"fixed","top":"0px","z-index":"9999"});
                var k = 0;
                jQuery("table#list_36_com_fabrik_36").width(window.tableWidth);
                jQuery("table#list_36_com_fabrik_36 thead tr th").each(function ()
                {
                    jQuery(this).width(window.hcol[k]);
                    k++;
                });
            }else{
                jQuery("table#list_36_com_fabrik_36 thead").css({"position":"relative","top":"0px","z-index":"9999"});
            }
        });
     
        /* resets header on window resize */
        jQuery(window).on('resize', function(){
            jQuery("table#list_36_com_fabrik_36 thead").css({"position":"relative","top":"0px","z-index":"9999"});
            setHeaders();
        });
     
        setHeaders();
     
        /* (Optional) This scrolls the table header to the top on load  */
        jQuery(this).scrollTop(window.eTop - window.headerHeight);
    });
     
    function setHeaders() {
        // initialize table header column widths
        window.hcol = new Array();
        var xwidth = 0;
        var k = 0;
        jQuery("table#list_36_com_fabrik_36 thead tr th").each(function (){
            xwidth = jQuery(this).width();
            window.hcol[k] = xwidth;
            k++;
        });
    }
     

    Attached Files:

  10. Bauer

    Bauer Well-Known Member

    Level: Supporter
    It looks fine to me - so I don't really know what to tell you. You sure you've cleared your browser cache since the edit?

    Does the javascript file show in firebug - and if you examine it through firebug is it the same as the file you edited?
    The file should be named list_36.js and be in your components/com_fabrik/js folder.
     
  11. d.bergonzi

    d.bergonzi Member

    Level: Community
    Oh...ok.....changing file naming list_36.js......javascript load correctly.....

    in attacch the result before and after down scrolling.... :( .......

    Furthermore there is also the possibility of fixing options (clear filter ... for Insert new item ... etc ...)?

    30-03-2015 10-02-42.png


    30-03-2015 10-02-42.png
     
  12. Bauer

    Bauer Well-Known Member

    Level: Supporter
    Either change your css background-color for 'table#list_36_com_fabrik_36 thead' (and/or) 'table#list_36_com_fabrik_36 thead tr' to something other than transparent in the list's css file - or change it via the javascript. That looks like your problem.

    My guess is there is no css set for the background - and so the intitial default of 'transparent' is being used. So, come to think of it, it would be easiest to just change (or add) the css you want for the thead tag in your css file. In that case, you can include all the style attributes except the "position" change there (and remove them from the jQuery css lines).

    Anyhow, it looks like you've got it.:cool:
     
  13. d.bergonzi

    d.bergonzi Member

    Level: Community
    Little by little I'm getting there. o_O
    Only I do not think that the problem is transparency (not only).

    If you know, in addition to the overlap, the columns are also slightly shifted to the right ....... causing a misalignment of the data.

    But at this point I think it is better to delegate to an expert (developer or web designer) the application of any changes to the CSS. correct? :confused:
     
  14. Bauer

    Bauer Well-Known Member

    Level: Supporter
    Well I never thought of using this for a list that had multiple rows in the header.
    Though I still think that simply assigning some css styling to the table will solve your problems.

    Or maybe the jQuery that is run to get the column widths should only include the 1st row?
    So the 2 places where you see
    jQuery("table#list_36_com_fabrik_36 thead tr th").each(function (){
    should be...
    jQuery("table#list_36_com_fabrik_36 thead tr:first th").each(function (){
    ???
    I haven't tested - but because there are multiple rows in the header, in your situation, that might fix it.
     
  15. Bauer

    Bauer Well-Known Member

    Level: Supporter
    I never used (or even saw) filters used in headers before (you learn something new with fabrik every day, I swear) - so your example threw me for a loop a few weeks back.

    But I finally got around to using them in one of my lists and needed to fix my fixed header script for that - and thought I'd pass it on - despite you giving up on me and going with stickyheaders (been there/done that).

    I switched the code around a bit so it will now work in fabrik tables that have filters embedded in the headers like your example.

    Normally the width of the headers would remain fixed - so until now I was just storing the original header column widths for use when the header was removed from the table and fixed to the top. But that won't work if you have filters in the header like you have here. So instead, I'm calculating the width of the first row of the table data itself and then use those widths for the fixed header.

    Then today I decided to add a fixed table footer also.
    Update 4/30/2015 to include check for hidden columns (if using list toggle column feature)

    I also included the requirejs fabrik function for inserting any watches for fabrik list events. (I think 'fabrik.list.update' should be all you need there.)

    Also note - I included some css for assigning a background color to the tr.fabrikFilterContainer (which is transparent by default - and was your other problem).

    As in the original post, just replace all instances of TABLEID in this code with the id of the table as it now appears in the html of your fabrik list - and you should be good to go.

    Read the comments in the code regarding how to disable things you don't want to use.
    Code (Text):
    jQuery(document).ready(function(){
     
        requirejs(['fab/fabrik'], function () {  
            window.eTop = jQuery("table#TABLEID tbody").offset().top;  
            window.eFoot = jQuery("table#TABLEID tfoot").offset().top;
           
            //assign background color for filter container (if transparent) - or remark out if not using filters in header
            jQuery("table#TABLEID thead tr.fabrikFilterContainer").css("background-color","#cecece");
     
            // this will refresh header on ajax update (display row limit changed, pagination, filter changed, etc.)
            Fabrik.addEvent('fabrik.list.update', function (block) {
                setHeaders();
            });  
           
            setHeaders();
        });  
       
        jQuery(window).scroll(function(event){
            window.bodyTop = jQuery(this).scrollTop();
            window.thisTop = window.eTop - jQuery(window).scrollTop(); //position of the ele window
            window.tableWidth = jQuery("table#TABLEID tbody.fabrik_groupdata").width();          
            // Use next line for debugging
            // console.log("headerHeight::"+window.headerHeight+" | bodyTop::" + window.bodyTop + " | eTop::"+window.eTop+" | thisTop::"+window.thisTop+" | eFoot::"+window.eFoot+" | footHeight::"+window.footHeight+" | dataHeight::"+window.dataHeight);
            if (window.thisTop < window.headerHeight ) {
                jQuery("table#TABLEID thead").css({"position":"fixed","top":"0px","z-index":"99"});
                var k = 0;
                jQuery("table#TABLEID thead").css("width",window.tableWidth+"px");
                jQuery("table#TABLEID>thead>tr.fabrik___heading>th").each(function (){
                    // if condition to ignore any columns hidden by column filters
                    if(jQuery(this).is(":visible")) {
                        jQuery(this).outerWidth(window.hcol[k]);              
                        k++;
                    }  
                });
            }else{
                jQuery("table#TABLEID thead").css({"position":"relative","top":"0px"});
            }
           
            // include this if you also want a sticky table footer
            if (window.bodyTop + window.eTop + window.headerHeight + window.footHeight < window.dataHeight)  
            {  
                jQuery("#TABLEID tfoot tr").css({"position":"fixed","bottom":"0","width":window.tableWidth+"px","z-index":"99"});
            }else{      
                jQuery("#TABLEID tfoot tr").css("position","relative");
            }
        });
     
        /* resets header on window resize */
        jQuery(window).on('resize', function(){
            jQuery("table#TABLEID>thead>tr.fabrik___heading>th").each(function (){
                jQuery(this).css("width","auto");              
            });      
            jQuery("table#TABLEID>tbody.fabrik_groupdata>tr.fabrik_row:eq(0)>td").each(function(){  
                thisClass = jQuery(this)[0].classList[0];
                jQuery("td[class^="+thisClass+"]").css("width","auto");  
            });      
            jQuery("table#TABLEID").css("width","100%");      
            setHeaders();
        });  
     
        /* (Optional) This scrolls the table header to the top on load  */
        jQuery(this).scrollTop(window.eTop - window.headerHeight);  
       
    });
     
     
    function setHeaders() {
        // 'lock' cell widths with css and initialize table header column widths
        jQuery("#TABLEID tfoot tr").css("position","relative");  
        jQuery("table#TABLEID thead").css({"position":"relative","top":"0px"});
        window.headerHeight = jQuery("table#TABLEID thead").innerHeight();
        window.dataHeight = jQuery("table#TABLEID tbody.fabrik_groupdata").height();
        window.footHeight = jQuery("#TABLEID tfoot tr").height();  
        window.tableWidth = jQuery("table#TABLEID tbody.fabrik_groupdata").width();
        // use next line for debugging
        //console.log("headerHeight::"+window.headerHeight+" | dataHeight::" + window.dataHeight + " | footHeight::"+window.footHeight+" | tableWidth::"+window.tableWidth);
        window.hcol = new Array();
        var xwidth = 0;
        var k = 0;
        var thisClass = '';
        jQuery("table#TABLEID>tbody.fabrik_groupdata>tr.fabrik_row:eq(0)>td").each(function(){  
            xwidth = jQuery(this).outerWidth();
            //console.log(xwidth);      
            thisClass = jQuery(this)[0].classList[0];
            jQuery("td[class^="+thisClass+"]").outerWidth(xwidth);  
            // if condition to ignore any columns hidden by column filters
            if(jQuery(this).is(":visible")) {
                // Depending on the #of columns or the width of the table you can try tweaking the width of the
                // individual header columns for best overall results here (change xwidth to xwidth-.5 - or whatever)
                window.hcol[k] = xwidth;
                k++;
            }      
        });
    }
     
  16. cheesegrits

    cheesegrits Support Gopher Staff Member

    Level: Community
    If you do ever find a decent way of doing fixed headers, let me know. I've tried every which way, and nothing I've ever found is anything but a giant hack.

    -- hugh
     
  17. Bauer

    Bauer Well-Known Member

    Level: Supporter
    Well I've been at this a few days now and this is working for me just fine ('hack' or not). This should work in any fabrik list really. Feel free to use that code and let me know how it works for you.

    And I just added sticky footers too.:)

    The issue d.bergonzi had was because he was using filters in the header - plus the transparency issue (which is easy enough to fix just with css).

    But by changing the way I calculate the column widths, even the issue with filters in the header is solved. Instead of just using the 'original' widths for each column from the header, I'm now getting the original widths for each column from the tbody <td>s. This way the column widths for both the table tbody <td>s and the detached headers <th>s stay in synch.

    And discovering the Fabrik.addEvent('fabrik.list.update', function() {...} was a godsend! That takes care of changes to filters or # of rows displayed - and pagination.

    My favorite clich? when it comes to using fabrik... "There's more than one way to skin a cat." I guess that's why I love it!:D

    See screen snips from one of my lists using that code in this thread... http://fabrikar.com/forums/index.php?threads/pagination-on-top-and-bottom-of-list.41231/
     
  18. d.bergonzi

    d.bergonzi Member

    Level: Community
    ok for solution.....
    I think that with easy modification on CSS the layout work corretctly.......but.......i have an other question...:)

    But.....you think you can also block the part related to option list (add ... clear filter group .... ..... etc .... etc ....) .....? .......I would also block logo and menu joomla but I think this is a matter of templating joomla

    Let me explain: I'm using the list contains many fields (columns) and I'd like to see the options even when I'm at the bottom of the list (left) invce than seeing them disappear off the monitor.
     
  19. Bauer

    Bauer Well-Known Member

    Level: Supporter
    You gonna have any room left on the page to show the rows?:p

    Fixing the menu bar at the top is a separate issue from fixing the fabrikFilterContainer.
    I'm using the MaxiMenuCK extension for my menus which already does that when the page is scrolled. (That's why 'we' had to add the z-index attribute for the list headers css - so it would overlay on top of the menu bar.)

    But you could also make it so that the top position for the 'fixed' list headers was below the menu bar - rather than zero.

    When I get some time, I'll play around with allowing some parameters to be initialized in this javascript code. Such as...
    1. Adding a variable that will act as a flag to determine if the div.fabrikFilterContainer should be included with (above) the fixed headers
    2. Adding a variable that will act as a flag to determine if the list title should be included above the fixed headers
    3. Specify the 'top' position for the fixed headers (in case there is already a fixed menu bar at the top and you want that to remain shown)

    I think that would pretty much include any additional 'features' needed for most users.

    Then I will just add the new code with those changes to the Wiki - per cheesegrits' suggestion a while back. (Another something that I've never done but have been meaning to learn how to do for a long time.)

    Unless you want to put me on your payroll, no promises for a time frame when I might get this done.;) - but it's on my 'to do' list.
     
  20. cheesegrits

    cheesegrits Support Gopher Staff Member

    Level: Community
    Thinking about it, my main problem in the past has been supporting <= IE8. I haven't tried since IE10 came out, meaning that technically we can get away with only supporting IE9/10. Next time I get a few free minutes, I'll give this solution a go.

    -- hugh
     

Share This Page