Fixed . Doesn't need any custom css/html. Does what position:sticky can't

Overview

jquery.floatThead

gif showing plugin in action

Documentation & Examples: http://mkoryak.github.io/floatThead/

Float the table header on scroll. No changes to your HTML/CSS are required, it just works. Supports floating the header while scrolling within the window or while scrolling within a container with overflow. Supports responsive tables.

Install

Package managers

npm install floatthead
bower install floatThead

Download code

Latest Release (zip)

Via CDN

https://cdnjs.com/libraries/floatthead/
https://www.jsdelivr.com/#!jquery.floatthead
https://unpkg.com/floatthead

<!-- Latest compiled and minified JavaScript -->
<script src="https://unpkg.com/floatthead"></script>
<script>
  $(() => $('table').floatThead());
</script>

For java people

Webjar

Wrappers

vuejs component by @tmlee

angularjs directive by @brandon-barker

yii2 framework wrapper by @bluezed

Why not just use position:sticky?


You probably should! This plugin was created years before that existed. There are still a few reasons why you might want to use this plugin:

  • Your code runs in the real world, where some browsers don't support position: sticky.
  • Any kind of non-standard scroll parent scenario, where the thing that you scroll with is not supported by position: sticky.
    • Your table's scroll parent isn't the body, but the body is what scrolls and you can't change this.
    • Your table scrolls horizontally within a container, but vertically within the page.
  • Your sticky top position is dynamic, or you want to know when the header becomes sticky and you don't want to write code to do this.
  • You don't want to learn these newfangled CSS things, you want a proven solution that works and uses jQuery, the greatest thing ever!

Things this plugin does:


  • In prod @ big corporations and opensource projects. Maintained. See open issues.
  • Works on tables within a scrollable container or whole window scrolling
  • Works with responsive table wrappers
  • Works with dynamically hidden/added/removed columns
  • Does not clone the thead - so your events stay bound
  • Does what position:fixed cannot do (and on browsers that do not support it)
  • Does not mess with your styles, and doesnt require any css (see fixed vs absolute position modes)
  • Works with border-collapse variants, weird margins, padding and borders
  • Works with libs like datatables, perfect-scrollbar, bootstrap3, and many more
  • Header can be floated with position:absolute which adds a wrapper, or position:fixed which does not. Both have their pros and cons. By default the best option is chosen based on your configuration

Things this plugin does NOT do:


  • Does not float the footer
  • Does not let you lock the first column like in excel
  • Safari and mobile safari are not supported. It might work, or it might not, depending on your markup and safari version.
  • RTL is not really supported - it might work in overflow scrolling more, if you are lucky. Expects dir on html element.
  • Layout issues resulting from document zoom not being 100% are not supported.

Common Pitfalls

If you use css and html best practices, this plugin will work. If you are stuck in 1999, you better read the faq.

How to get help with the floatThead

All issues should be reported through github.

Requirements:

  • jQuery 1.8.x or better (1.9 compliant) (or jQuery 1.7.x and jQuery UI core)

Supported Browsers:

Change Log

see CHANGELOG.md

Who is using floatThead ?

Ctrl O

  • Ctrl O provides simple and innovative products to help an organization's business processes. Linkspace, its flagship product, helps share information between teams and individuals, in a simple and effective manner.

WheresTheGig.com

  • A free service for the musical community

Google

  • Internally, I happen to know...

tld-list.com

  • The first table you see.

Samsung

  • For the internet of things!

Around 153,000 hits on github code search

License

MIT

Comments
  • html thead is always visible at the top of the screen not when scrolling down

    html thead is always visible at the top of the screen not when scrolling down

    I am working in a rails app. I have a table on my screen and the table header always stays at the top doesn't move when i am scrolling down. I downloaded JQuery.floatThead plugin and required it in application.js script by doing //= require jquery_floatThead and then in the application.js file i called floatThead function but its not working. I am also using bootstrap. Here is my application.js file:

    // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. // // // this brings in jquery, bootstrap, and all the defaults

    // // Note that usually, Rails uses require_tree . here. That requires // everything in the current directory tree. I don't think we reallg // want to do that for this app, though, because there will be a bid // variety of things deployed. // // However, you probably DO want to require almost everything here, so // it gets compiled into the one giant cached JS file. That is better // than separate files for every page, usually. // //

    PNotify.prototype.options.styling = "fontawesome";

    // Set the auth token on all ajax requests, if we have one $(document).ready(function() { if (window.bpub.authToken) { var token = encodeURIComponent(window.bpub.authToken); $.ajaxSetup({ headers: {'Authorization' : 'Token token="' + token + '"'}, }); } }); /* Global error handler for all ajax problems. If you don't want this, because you want to handle it yourself, then add the global: false option as follows:

    $.ajax({ url: url, data: data, global: false, ... })

    */ $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) { var message = jqxhr.responseText; if (jqxhr.responseJSON.error && jqxhr.responseJSON.error.message) { message = jqxhr.responseJSON.error.message; }

    new PNotify({
        type: 'error',
        title: 'Error',
        text: 'Error: ' + message,
    });
    

    });

     var $table = $(".queue-display");
    
     $table.floatThead({
    
        //absuoltePositioning is better for highly dynamic site
        useAbsolutePositioning: false
    
    
     });
    

    $(document).ready(function() {

    // used to make a simple popup -- put data-popup-name="whatever"
    // in your <a>, and clicking on the a will open a window with that
    // name
    
    $(document).on("click", 'a[data-popup-name]', function(event) {
        event.preventDefault();
    
        var name = $(this).data('popup-name');
        var win = window.open($(this).attr("href"), name, "width=600,height=600,scrollbars=yes");
        win.focus();
    });
    
    // helper for adding functionality that doesn't really exist yet
    $(document).on("click", ".not-rly", function() {
        alert("Not implemented yet");
    });
    
    // TODO: better way to handle these tool tips?
    $('[data-toggle="tooltip"]').tooltip();
    $(document).bind("ajaxComplete", function(){
        $('[data-toggle="tooltip"]').tooltip();
    });
    

    });

    // clean up all our yamm windows -- if they are long then they won't // fit on the page properly.

    $(document).ready(function() { var resize_yamm = function() { // 150 is somewhat arbitrary, want to fill up most of screen // but still look like a dropdown, and don't overlap toolbars

        var height = $(window).height() - 150;
    
        $('.yamm-content').css('max-height', height + 'px');
    }
    
    resize_yamm();
    
    $(window).resize(resize_yamm);
    

    });

    // this will catch any error and email it to support $(document).ready(function() { window.onerror = function(errorMsg, url, lineNumber) { var postUrl = window.bpub.queue_web_services_base + "/errors/send_to_support";

        var message = "Error in bpub-web: " + errorMsg;
    
        message = message + "\n\n" + url + "\nline: " + lineNumber;
    

    // disabling for now -- this is spamming // $.ajax( // { url: postUrl, // data: { "message" : message }, // method: 'post', // } // ); }; });

    $(document).ready(function() {

    // some crazy hack to stop IE from selecting text when you do a
    // 'shift-click'
    $('.unselectable').mousedown(function(e) {
        if (e.ctrlKey || e.shiftKey) {
            e.preventDefault();
    
            this.onselectstart = function () { return false; };
            var self = this;
            window.setTimeout(function() { self.onselectstart = null;}, 0);
        }
    });
    

    });

    $(document).ready(function() { tw_attach_data_link_clicks(window.bpub.queue_web_services_base, window.bpub.current_user); });

    // This handles automatically saving a field. Stick an attribute on // an input, and when it changes this will automatically store it to // localstorage. When the page is loaded, then we update the field // with the data from local storage $(document).ready(function() {

    // first restore them all
    $('[data-auto-save]').each(function(i, item) {
        var saveName = $(item).data('auto-save');
        var content =localStorage.getItem(saveName);
    
        if (content) {
            $(item).val(content);
        }
    });
    
    $('[data-auto-save]').on('change', function() {
        var saveName = $(this).data('auto-save');
        localStorage.setItem(saveName, $(this).val());
    });
    

    });

    Here is my jquery.float plugin:

    // @preserve jQuery.floatThead 1.2.12 - http://mkoryak.github.io/floatThead/ - Copyright (c) 2012 - 2015 Misha Koryak // @license MIT

    /* @author Misha Koryak

    • @projectDescription lock a table header in place while scrolling - without breaking styles or events bound to the header *
    • Dependencies:
    • jquery 1.9.0 + [required] OR jquery 1.7.0 + jquery UI core *
    • http://mkoryak.github.io/floatThead/ *
    • Tested on FF13+, Chrome 21+, IE8, IE9, IE10, IE11 * */

    (function( $ ) { /**

    • provides a default config object. You can modify this after including this script if you want to change the init defaults

    • @type {Object} / $.floatThead = $.floatThead || {}; $.floatThead.defaults = { cellTag: null, // DEPRECATED - use headerCellSelector instead headerCellSelector: 'tr:visible:first>:visible', //thead cells are this. zIndex: 1001, //zindex of the floating thead (actually a container div) debounceResizeMs: 10, //Deprecated! useAbsolutePositioning: true, //if set to NULL - defaults: has scrollContainer=true, doesn't have scrollContainer=false scrollingTop: 0, //String or function($table) - offset from top of window where the header should not pass above scrollingBottom: 0, //String or function($table) - offset from the bottom of the table where the header should stop scrolling scrollContainer: function($table){ return $([]); //if the table has horizontal scroll bars then this is the container that has overflow:auto and causes those scroll bars }, getSizingRow: function($table, $cols, $fthCells){ // this is only called when using IE, // override it if the first row of the table is going to contain colgroups (any cell spans greater than one col) // it should return a jquery object containing a wrapped set of table cells comprising a row that contains no col spans and is visible return $table.find('tbody tr:visible:first>*:visible'); }, floatTableClass: 'floatThead-table', floatWrapperClass: 'floatThead-wrapper', floatContainerClass: 'floatThead-container', copyTableClass: true, //copy 'class' attribute from table into the floated table so that the styles match. enableAria: false, //will copy header text from the floated header back into the table for screen readers. Might cause the css styling to be off. beware! autoReflow: false, //(undocumented) - use MutationObserver api to reflow automatically when internal table DOM changes debug: false //print possible issues (that don't prevent script loading) to console, if console exists. };

      var util = window._;

      var canObserveMutations = typeof MutationObserver !== 'undefined';

      //browser stuff var ieVersion = function(){for(var a=3,b=document.createElement("b"),c=b.all||[];a = 1+a,b.innerHTML="",c[0];);return 4<a?a:document.documentMode}(); var isFF = /Gecko//.test(navigator.userAgent); var isWebkit = /WebKit//.test(navigator.userAgent);

      //safari 7 (and perhaps others) reports table width to be parent container's width if max-width is set on table. see: https://github.com/mkoryak/floatThead/issues/108 var isTableWidthBug = function(){ if(isWebkit) { var $test = $('

      X
      '); $("body").append($test); var ret = ($test.find("table").width() == 0); $test.remove(); return ret; } return false; };

      var createElements = !isFF && !ieVersion; //FF can read width from elements, but webkit cannot

      var $window = $(window);

      /**

    • @param debounceMs

    • @param cb */ function windowResize(debounceMs, eventName, cb){ if(ieVersion == 8){ //ie8 is crap: https://github.com/mkoryak/floatThead/issues/65 var winWidth = $window.width(); var debouncedCb = util.debounce(function(){ var winWidthNew = $window.width(); if(winWidth != winWidthNew){ winWidth = winWidthNew; cb(); } }, debounceMs); $window.on(eventName, debouncedCb); } else { $window.on(eventName, util.debounce(cb, debounceMs)); } }

      function debug(str){ window && window.console && window.console.log && window.console.log("jQuery.floatThead: " + str); }

      //returns fractional pixel widths function getOffsetWidth(el) { var rect = el.getBoundingClientRect(); return rect.width || rect.right - rect.left; }

      /**

    • try to calculate the scrollbar width for your browser/os

    • @return {Number} / function scrollbarWidth() { var $div = $( //borrowed from anti-scroll '

      ' + '
      ' ); $('body').append($div); var w1 = $div.innerWidth(); var w2 = $('div', $div).innerWidth(); $div.remove(); return w1 - w2; } /*

    • Check if a given table has been datatableized (http://datatables.net)

    • @param $table

    • @return {Boolean} */ function isDatatable($table){ if($table.dataTableSettings){ for(var i = 0; i < $table.dataTableSettings.length; i++){ var table = $table.dataTableSettings[i].nTable; if($table[0] == table){ return true; } } } return false; }

      function tableWidth($table, $fthCells, isOuter){ // see: https://github.com/mkoryak/floatThead/issues/108 var fn = isOuter ? "outerWidth": "width"; if(isTableWidthBug && $table.css("max-width")){ var w = 0; if(isOuter) { w += parseInt($table.css("borderLeft"), 10); w += parseInt($table.css("borderRight"), 10); } for(var i=0; i < $fthCells.length; i++){ w += $fthCells.get(i).offsetWidth; } return w; } else { return $tablefn; } } $.fn.floatThead = function(map){ map = map || {}; if(!util){ //may have been included after the script? lets try to grab it again. util = window._ || $.floatThead._; if(!util){ throw new Error("jquery.floatThead-slim.js requires underscore. You should use the non-lite version since you do not have underscore."); } }

      if(ieVersion < 8){ return this; //no more crappy browser support. }

      var mObs = null; //mutation observer lives in here if we can use it / make it

      if(util.isFunction(isTableWidthBug)) { isTableWidthBug = isTableWidthBug(); }

      if(util.isString(map)){ var command = map; var ret = this; this.filter('table').each(function(){ var $this = $(this); var opts = $this.data('floatThead-lazy'); if(opts){ $this.floatThead(opts); } var obj = $this.data('floatThead-attached'); if(obj && util.isFunction(obj[command])){ var r = objcommand; if(typeof r !== 'undefined'){ ret = r; } } }); return ret; } var opts = $.extend({}, $.floatThead.defaults || {}, map);

      $.each(map, function(key, val){ if((!(key in $.floatThead.defaults)) && opts.debug){ debug("Used ["+key+"] key to init plugin, but that param is not an option for the plugin. Valid options are: "+ (util.keys($.floatThead.defaults)).join(', ')); } }); if(opts.debug){ var v = $.fn.jquery.split("."); if(parseInt(v[0], 10) == 1 && parseInt(v[1], 10) <= 7){ debug("jQuery version "+$.fn.jquery+" detected! This plugin supports 1.8 or better, or 1.7.x with jQuery UI 1.8.24 -> http://jqueryui.com/resources/download/jquery-ui-1.8.24.zip") } }

      this.filter(':not(.'+opts.floatTableClass+')').each(function(){ var floatTheadId = util.uniqueId(); var $table = $(this); if($table.data('floatThead-attached')){ return true; //continue the each loop } if(!$table.is('table')){ throw new Error('jQuery.floatThead must be run on a table element. ex: $("table").floatThead();'); } canObserveMutations = opts.autoReflow && canObserveMutations; //option defaults to false! var $header = $table.children('thead:first'); var $tbody = $table.children('tbody:first'); if($header.length == 0 || $tbody.length == 0){ $table.data('floatThead-lazy', opts); $table.one('reflow', function(){ $table.floatThead(opts); }); return; } if($table.data('floatThead-lazy')){ $table.unbind("reflow"); } $table.data('floatThead-lazy', false);

      var headerFloated = false;
      var scrollingTop, scrollingBottom;
      var scrollbarOffset = {vertical: 0, horizontal: 0};
      var scWidth = scrollbarWidth();
      var lastColumnCount = 0; //used by columnNum()
      var $scrollContainer = opts.scrollContainer($table) || $([]); //guard against returned nulls
      var locked = $scrollContainer.length > 0;
      
      var useAbsolutePositioning = opts.useAbsolutePositioning;
      if(useAbsolutePositioning == null){ //defaults: locked=true, !locked=false
        useAbsolutePositioning = locked;
      }
      if(!useAbsolutePositioning){
        headerFloated = true; //#127
      }
      var $caption = $table.find("caption");
      var haveCaption = $caption.length == 1;
      if(haveCaption){
        var captionAlignTop = ($caption.css("caption-side") || $caption.attr("align") || "top") === "top";
      }
      
      var $fthGrp = $('<fthfoot style="display:table-footer-group;border-spacing:0;height:0;border-collapse:collapse;"/>');
      
      var wrappedContainer = false; //used with absolute positioning enabled. did we need to wrap the scrollContainer/table with a relative div?
      var $wrapper = $([]); //used when absolute positioning enabled - wraps the table and the float container
      var absoluteToFixedOnScroll = ieVersion <= 9 && !locked && useAbsolutePositioning; //on IE using absolute positioning doesn't look good with window scrolling, so we change position to fixed on scroll, and then change it back to absolute when done.
      var $floatTable = $("<table/>");
      var $floatColGroup = $("<colgroup/>");
      var $tableColGroup = $table.children('colgroup:first');
      var existingColGroup = true;
      if($tableColGroup.length == 0){
        $tableColGroup = $("<colgroup/>");
        existingColGroup = false;
      }
      var $fthRow = $('<fthtr style="display:table-row;border-spacing:0;height:0;border-collapse:collapse"/>'); //created unstyled elements (used for sizing the table because chrome can't read <col> width)
      var $floatContainer = $('<div style="overflow: hidden;" aria-hidden="true" class="floatThead-floatContainer"></div>');
      var floatTableHidden = false; //this happens when the table is hidden and we do magic when making it visible
      var $newHeader = $("<thead/>");
      var $sizerRow = $('<tr class="size-row"/>');
      var $sizerCells = $([]);
      var $tableCells = $([]); //used for sizing - either $sizerCells or $tableColGroup cols. $tableColGroup cols are only created in chrome for borderCollapse:collapse because of a chrome bug.
      var $headerCells = $([]);
      var $fthCells = $([]); //created elements
      
      $newHeader.append($sizerRow);
      $table.prepend($tableColGroup);
      if(createElements){
        $fthGrp.append($fthRow);
        $table.append($fthGrp);
      }
      
      $floatTable.append($floatColGroup);
      $floatContainer.append($floatTable);
      if(opts.copyTableClass){
        $floatTable.attr('class', $table.attr('class'));
      }
      $floatTable.attr({ //copy over some deprecated table attributes that people still like to use. Good thing people don't use colgroups...
        'cellpadding': $table.attr('cellpadding'),
        'cellspacing': $table.attr('cellspacing'),
        'border': $table.attr('border')
      });
      var tableDisplayCss = $table.css('display');
      $floatTable.css({
        'borderCollapse': $table.css('borderCollapse'),
        'border': $table.css('border'),
        'display': tableDisplayCss
      });
      if(tableDisplayCss == 'none'){
        floatTableHidden = true;
      }
      
      $floatTable.addClass(opts.floatTableClass).css({'margin': 0, 'border-bottom-width': 0}); //must have no margins or you won't be able to click on things under floating table
      
      if(useAbsolutePositioning){
        var makeRelative = function($container, alwaysWrap){
          var positionCss = $container.css('position');
          var relativeToScrollContainer = (positionCss == "relative" || positionCss == "absolute");
          if(!relativeToScrollContainer || alwaysWrap){
            var css = {"paddingLeft": $container.css('paddingLeft'), "paddingRight": $container.css('paddingRight')};
            $floatContainer.css(css);
            $container = $container.wrap("<div class='"+opts.floatWrapperClass+"' style='position: relative; clear:both;'></div>").parent();
            wrappedContainer = true;
          }
          return $container;
        };
        if(locked){
          $wrapper = makeRelative($scrollContainer, true);
          $wrapper.append($floatContainer);
        } else {
          $wrapper = makeRelative($table);
          $table.after($floatContainer);
        }
      } else {
        $table.after($floatContainer);
      }
      
      
      $floatContainer.css({
        position: useAbsolutePositioning ? 'absolute' : 'fixed',
        marginTop: 0,
        top:  useAbsolutePositioning ? 0 : 'auto',
        zIndex: opts.zIndex
      });
      $floatContainer.addClass(opts.floatContainerClass);
      updateScrollingOffsets();
      
      var layoutFixed = {'table-layout': 'fixed'};
      var layoutAuto = {'table-layout': $table.css('tableLayout') || 'auto'};
      var originalTableWidth = $table[0].style.width || ""; //setting this to auto is bad: #70
      var originalTableMinWidth = $table.css('minWidth') || "";
      
      function eventName(name){
        return name+'.fth-'+floatTheadId+'.floatTHead'
      }
      
      function setHeaderHeight(){
        var headerHeight = 0;
        $header.children("tr:visible").each(function(){
          headerHeight += $(this).outerHeight(true);
        });
        if($table.css('border-collapse') == 'collapse') {
          var tableBorderTopHeight = parseInt($table.css('border-top-width'), 10);
          var cellBorderTopHeight = parseInt($table.find("thead tr:first").find(">*:first").css('border-top-width'), 10);
          if(tableBorderTopHeight > cellBorderTopHeight) {
            headerHeight -= (tableBorderTopHeight / 2); //id love to see some docs where this magic recipe is found..
          }
        }
        $sizerRow.outerHeight(headerHeight);
        $sizerCells.outerHeight(headerHeight);
      }
      
      
      function setFloatWidth(){
        var tw = tableWidth($table, $fthCells, true);
        var width = $scrollContainer.width() || tw;
        var floatContainerWidth = $scrollContainer.css("overflow-y") != 'hidden' ? width - scrollbarOffset.vertical : width;
        $floatContainer.width(floatContainerWidth);
        if(locked){
          var percent = 100 * tw / (floatContainerWidth);
          $floatTable.css('width', percent+'%');
        } else {
          $floatTable.outerWidth(tw);
        }
      }
      
      function updateScrollingOffsets(){
        scrollingTop = (util.isFunction(opts.scrollingTop) ? opts.scrollingTop($table) : opts.scrollingTop) || 0;
        scrollingBottom = (util.isFunction(opts.scrollingBottom) ? opts.scrollingBottom($table) : opts.scrollingBottom) || 0;
      }
      
      /**
       * get the number of columns and also rebuild resizer rows if the count is different than the last count
       */
      function columnNum(){
        var count, $headerColumns;
        if(existingColGroup){
          count = $tableColGroup.find('col').length;
        } else {
          var selector;
          if(opts.cellTag == null && opts.headerCellSelector){ //TODO: once cellTag option is removed, remove this conditional
            selector = opts.headerCellSelector;
          } else {
            selector = 'tr:first>'+opts.cellTag;
          }
          if(util.isNumber(selector)){
            //it's actually a row count. (undocumented, might be removed!)
            return selector;
          }
          $headerColumns = $header.find(selector);
          count = 0;
          $headerColumns.each(function(){
            count += parseInt(($(this).attr('colspan') || 1), 10);
          });
        }
        if(count != lastColumnCount){
          lastColumnCount = count;
          var cells = [], cols = [], psuedo = [], content;
          for(var x = 0; x < count; x++){
            if (opts.enableAria && (content = $headerColumns.eq(x).text()) ) {
              cells.push('<th scope="col" class="floatThead-col">' + content + '</th>');
            } else {
              cells.push('<th class="floatThead-col"/>');
            }
            cols.push('<col/>');
            psuedo.push("<fthtd style='display:table-cell;height:0;width:auto;'/>");
          }
      
          cols = cols.join('');
          cells = cells.join('');
      
          if(createElements){
            psuedo = psuedo.join('');
            $fthRow.html(psuedo);
            $fthCells = $fthRow.find('fthtd');
          }
      
          $sizerRow.html(cells);
          $sizerCells = $sizerRow.find("th");
          if(!existingColGroup){
            $tableColGroup.html(cols);
          }
          $tableCells = $tableColGroup.find('col');
          $floatColGroup.html(cols);
          $headerCells = $floatColGroup.find("col");
      
        }
        return count;
      }
      
      function refloat(){ //make the thing float
        if(!headerFloated){
          headerFloated = true;
          if(useAbsolutePositioning){ //#53, #56
            var tw = tableWidth($table, $fthCells);
            var wrapperWidth = $wrapper.width();
            if(tw > wrapperWidth){
              $table.css('minWidth', tw);
            }
          }
          $table.css(layoutFixed);
          $floatTable.css(layoutFixed);
          $floatTable.append($header); //append because colgroup must go first in chrome
          $tbody.before($newHeader);
          setHeaderHeight();
        }
      }
      function unfloat(){ //put the header back into the table
        if(headerFloated){
          headerFloated = false;
          if(useAbsolutePositioning){ //#53, #56
            $table.width(originalTableWidth);
          }
          $newHeader.detach();
          $table.prepend($header);
          $table.css(layoutAuto);
          $floatTable.css(layoutAuto);
          $table.css('minWidth', originalTableMinWidth); //this looks weird, but it's not a bug. Think about it!!
          $table.css('minWidth', tableWidth($table, $fthCells)); //#121
        }
      }
      var isHeaderFloatingLogical = false; //for the purpose of this event, the header is/isnt floating, even though the element
                                           //might be in some other state. this is what the header looks like to the user
      function triggerFloatEvent(isFloating){
        if(isHeaderFloatingLogical != isFloating){
          isHeaderFloatingLogical = isFloating;
          $table.triggerHandler("floatThead", [isFloating, $floatContainer])
        }
      }
      function changePositioning(isAbsolute){
        if(useAbsolutePositioning != isAbsolute){
          useAbsolutePositioning = isAbsolute;
          $floatContainer.css({
            position: useAbsolutePositioning ? 'absolute' : 'fixed'
          });
        }
      }
      function getSizingRow($table, $cols, $fthCells, ieVersion){
        if(createElements){
          return $fthCells;
        } else if(ieVersion) {
          return opts.getSizingRow($table, $cols, $fthCells);
        } else {
          return $cols;
        }
      }
      
      /**
       * returns a function that updates the floating header's cell widths.
       * @return {Function}
       */
      function reflow(){
        var i;
        var numCols = columnNum(); //if the tables columns changed dynamically since last time (datatables), rebuild the sizer rows and get a new count
      
        return function(){
          $tableCells = $tableColGroup.find('col');
          var $rowCells = getSizingRow($table, $tableCells, $fthCells, ieVersion);
      
          if($rowCells.length == numCols && numCols > 0){
            if(!existingColGroup){
              for(i=0; i < numCols; i++){
                $tableCells.eq(i).css('width', '');
              }
            }
            unfloat();
            var widths = [];
            for(i=0; i < numCols; i++){
              widths[i] = getOffsetWidth($rowCells.get(i));
            }
            for(i=0; i < numCols; i++){
              $headerCells.eq(i).width(widths[i]);
              $tableCells.eq(i).width(widths[i]);
            }
            refloat();
          } else {
            $floatTable.append($header);
            $table.css(layoutAuto);
            $floatTable.css(layoutAuto);
            setHeaderHeight();
          }
        };
      }
      
      function floatContainerBorderWidth(side){
        var border = $scrollContainer.css("border-"+side+"-width");
        var w = 0;
        if (border && ~border.indexOf('px')) {
          w = parseInt(border, 10);
        }
        return w;
      }
      /**
       * first performs initial calculations that we expect to not change when the table, window, or scrolling container are scrolled.
       * returns a function that calculates the floating container's top and left coords. takes into account if we are using page scrolling or inner scrolling
       * @return {Function}
       */
      function calculateFloatContainerPosFn(){
        var scrollingContainerTop = $scrollContainer.scrollTop();
      
        //this floatEnd calc was moved out of the returned function because we assume the table height doesn't change (otherwise we must reinit by calling calculateFloatContainerPosFn)
        var floatEnd;
        var tableContainerGap = 0;
        var captionHeight = haveCaption ? $caption.outerHeight(true) : 0;
        var captionScrollOffset = captionAlignTop ? captionHeight : -captionHeight;
      
        var floatContainerHeight = $floatContainer.height();
        var tableOffset = $table.offset();
        var tableLeftGap = 0; //can be caused by border on container (only in locked mode)
        var tableTopGap = 0;
        if(locked){
          var containerOffset = $scrollContainer.offset();
          tableContainerGap = tableOffset.top - containerOffset.top + scrollingContainerTop;
          if(haveCaption && captionAlignTop){
            tableContainerGap += captionHeight;
          }
          tableLeftGap = floatContainerBorderWidth('left');
          tableTopGap = floatContainerBorderWidth('top');
          tableContainerGap -= tableTopGap;
        } else {
          floatEnd = tableOffset.top - scrollingTop - floatContainerHeight + scrollingBottom + scrollbarOffset.horizontal;
        }
        var windowTop = $window.scrollTop();
        var windowLeft = $window.scrollLeft();
        var scrollContainerLeft =  $scrollContainer.scrollLeft();
      
        return function(eventType){
          var isTableHidden = $table[0].offsetWidth <= 0 && $table[0].offsetHeight <= 0;
          if(!isTableHidden && floatTableHidden) {
            floatTableHidden = false;
            setTimeout(function(){
              $table.triggerHandler("reflow");
            }, 1);
            return null;
          }
          if(isTableHidden){ //it's hidden
            floatTableHidden = true;
            if(!useAbsolutePositioning){
              return null;
            }
          }
      
          if(eventType == 'windowScroll'){
            windowTop = $window.scrollTop();
            windowLeft = $window.scrollLeft();
          } else if(eventType == 'containerScroll'){
            scrollingContainerTop = $scrollContainer.scrollTop();
            scrollContainerLeft =  $scrollContainer.scrollLeft();
          } else if(eventType != 'init') {
            windowTop = $window.scrollTop();
            windowLeft = $window.scrollLeft();
            scrollingContainerTop = $scrollContainer.scrollTop();
            scrollContainerLeft =  $scrollContainer.scrollLeft();
          }
          if(isWebkit && (windowTop < 0 || windowLeft < 0)){ //chrome overscroll effect at the top of the page - breaks fixed positioned floated headers
            return;
          }
      
          if(absoluteToFixedOnScroll){
            if(eventType == 'windowScrollDone'){
              changePositioning(true); //change to absolute
            } else {
              changePositioning(false); //change to fixed
            }
          } else if(eventType == 'windowScrollDone'){
            return null; //event is fired when they stop scrolling. ignore it if not 'absoluteToFixedOnScroll'
          }
      
          tableOffset = $table.offset();
          if(haveCaption && captionAlignTop){
            tableOffset.top += captionHeight;
          }
          var top, left;
          var tableHeight = $table.outerHeight();
      
          if(locked && useAbsolutePositioning){ //inner scrolling, absolute positioning
            if (tableContainerGap >= scrollingContainerTop) {
              var gap = tableContainerGap - scrollingContainerTop + tableTopGap;
              top = gap > 0 ? gap : 0;
              triggerFloatEvent(false);
            } else {
              top = wrappedContainer ? tableTopGap : scrollingContainerTop;
              //headers stop at the top of the viewport
              triggerFloatEvent(true);
            }
            left = tableLeftGap;
          } else if(!locked && useAbsolutePositioning) { //window scrolling, absolute positioning
            if(windowTop > floatEnd + tableHeight + captionScrollOffset){
              top = tableHeight - floatContainerHeight + captionScrollOffset; //scrolled past table
            } else if (tableOffset.top >= windowTop + scrollingTop) {
              top = 0; //scrolling to table
              unfloat();
              triggerFloatEvent(false);
            } else {
              top = scrollingTop + windowTop - tableOffset.top + tableContainerGap + (captionAlignTop ? captionHeight : 0);
              refloat(); //scrolling within table. header floated
              triggerFloatEvent(true);
            }
            left =  0;
          } else if(locked && !useAbsolutePositioning){ //inner scrolling, fixed positioning
            if (tableContainerGap > scrollingContainerTop || scrollingContainerTop - tableContainerGap > tableHeight) {
              top = tableOffset.top - windowTop;
              unfloat();
              triggerFloatEvent(false);
            } else {
              top = tableOffset.top + scrollingContainerTop  - windowTop - tableContainerGap;
              refloat();
              triggerFloatEvent(true);
              //headers stop at the top of the viewport
            }
            left = tableOffset.left + scrollContainerLeft - windowLeft;
          } else if(!locked && !useAbsolutePositioning) { //window scrolling, fixed positioning
            if(windowTop > floatEnd + tableHeight + captionScrollOffset){
              top = tableHeight + scrollingTop - windowTop + floatEnd + captionScrollOffset;
              //scrolled past the bottom of the table
            } else if (tableOffset.top > windowTop + scrollingTop) {
              top = tableOffset.top - windowTop;
              refloat();
              triggerFloatEvent(false); //this is a weird case, the header never gets unfloated and i have no no way to know
              //scrolled past the top of the table
            } else {
              //scrolling within the table
              top = scrollingTop;
              triggerFloatEvent(true);
            }
            left = tableOffset.left - windowLeft;
          }
          return {top: top, left: left};
        };
      }
      /**
       * returns a function that caches old floating container position and only updates css when the position changes
       * @return {Function}
       */
      function repositionFloatContainerFn(){
        var oldTop = null;
        var oldLeft = null;
        var oldScrollLeft = null;
        return function(pos, setWidth, setHeight){
          if(pos != null && (oldTop != pos.top || oldLeft != pos.left)){
            $floatContainer.css({
              top: pos.top,
              left: pos.left
            });
            oldTop = pos.top;
            oldLeft = pos.left;
          }
          if(setWidth){
            setFloatWidth();
          }
          if(setHeight){
            setHeaderHeight();
          }
          var scrollLeft = $scrollContainer.scrollLeft();
          if(!useAbsolutePositioning || oldScrollLeft != scrollLeft){
            $floatContainer.scrollLeft(scrollLeft);
            oldScrollLeft = scrollLeft;
          }
        }
      }
      
      /**
       * checks if THIS table has scrollbars, and finds their widths
       */
      function calculateScrollBarSize(){ //this should happen after the floating table has been positioned
        if($scrollContainer.length){
          if($scrollContainer.data().perfectScrollbar){
            scrollbarOffset = {horizontal:0, vertical:0};
          } else {
            var sw = $scrollContainer.width(), sh = $scrollContainer.height(), th = $table.height(), tw = tableWidth($table, $fthCells);
            var offseth = sw < tw ? scWidth : 0;
            var offsetv = sh < th ? scWidth : 0;
            scrollbarOffset.horizontal = sw - offsetv < tw ? scWidth : 0;
            scrollbarOffset.vertical = sh - offseth < th ? scWidth : 0;
          }
        }
      }
      //finish up. create all calculation functions and bind them to events
      calculateScrollBarSize();
      
      var flow;
      
      var ensureReflow = function(){
        flow = reflow();
        flow();
      };
      
      ensureReflow();
      
      var calculateFloatContainerPos = calculateFloatContainerPosFn();
      var repositionFloatContainer = repositionFloatContainerFn();
      
      repositionFloatContainer(calculateFloatContainerPos('init'), true); //this must come after reflow because reflow changes scrollLeft back to 0 when it rips out the thead
      
      var windowScrollDoneEvent = util.debounce(function(){
        repositionFloatContainer(calculateFloatContainerPos('windowScrollDone'), false);
      }, 1);
      
      var windowScrollEvent = function(){
        repositionFloatContainer(calculateFloatContainerPos('windowScroll'), false);
        if(absoluteToFixedOnScroll){
          windowScrollDoneEvent();
        }
      };
      var containerScrollEvent = function(){
        repositionFloatContainer(calculateFloatContainerPos('containerScroll'), false);
      };
      
      
      var windowResizeEvent = function(){
        updateScrollingOffsets();
        calculateScrollBarSize();
        ensureReflow();
        calculateFloatContainerPos = calculateFloatContainerPosFn();
        repositionFloatContainer = repositionFloatContainerFn();
        repositionFloatContainer(calculateFloatContainerPos('resize'), true, true);
      };
      var reflowEvent = util.debounce(function(){
        calculateScrollBarSize();
        updateScrollingOffsets();
        ensureReflow();
        calculateFloatContainerPos = calculateFloatContainerPosFn();
        repositionFloatContainer(calculateFloatContainerPos('reflow'), true);
      }, 1);
      if(locked){ //internal scrolling
        if(useAbsolutePositioning){
          $scrollContainer.on(eventName('scroll'), containerScrollEvent);
        } else {
          $scrollContainer.on(eventName('scroll'), containerScrollEvent);
          $window.on(eventName('scroll'), windowScrollEvent);
        }
      } else { //window scrolling
        $window.on(eventName('scroll'), windowScrollEvent);
      }
      
      $window.on(eventName('load'), reflowEvent); //for tables with images
      
      windowResize(opts.debounceResizeMs, eventName('resize'), windowResizeEvent);
      $table.on('reflow', reflowEvent);
      if(isDatatable($table)){
        $table
          .on('filter', reflowEvent)
          .on('sort',   reflowEvent)
          .on('page',   reflowEvent);
      }
      
      
      if (canObserveMutations) {
        var mutationElement = $scrollContainer.length ? $scrollContainer[0] : $table[0];
        mObs = new MutationObserver(function(e){
          var wasThead = function(nodes){
            return nodes && nodes[0] && nodes[0].nodeName == "THEAD";
          };
          for(var i=0; i < e.length; i++){
            if(!(wasThead(e[i].addedNodes) || wasThead(e[i].removedNodes))){
              reflowEvent();
              break;
            }
          }
        });
        mObs.observe(mutationElement, {
            childList: true,
            subtree: true
        });
      }
      
      //attach some useful functions to the table.
      $table.data('floatThead-attached', {
        destroy: function(){
          var ns = '.fth-'+floatTheadId;
          unfloat();
          $table.css(layoutAuto);
          $tableColGroup.remove();
          createElements && $fthGrp.remove();
          if($newHeader.parent().length){ //only if it's in the DOM
            $newHeader.replaceWith($header);
          }
          if(canObserveMutations){
            mObs.disconnect();
            mObs = null;
          }
          $table.off('reflow');
          $scrollContainer.off(ns);
          if (wrappedContainer) {
            if ($scrollContainer.length) {
              $scrollContainer.unwrap();
            }
            else {
              $table.unwrap();
            }
          }
          $table.css('minWidth', originalTableMinWidth);
          $floatContainer.remove();
          $table.data('floatThead-attached', false);
          $window.off(ns);
        },
        reflow: function(){
          reflowEvent();
        },
        setHeaderHeight: function(){
          setHeaderHeight();
        },
        getFloatContainer: function(){
          return $floatContainer;
        },
        getRowGroups: function(){
          if(headerFloated){
            return $floatContainer.children("thead").add($table.children("tbody,tfoot"));
          } else {
            return $table.children("thead,tbody,tfoot");
          }
        }
      });
      

      }); return this; }; })(jQuery); /* jQuery.floatThead.utils - http://mkoryak.github.io/floatThead/ - Copyright (c) 2012 - 2014 Misha Koryak

      • License: MIT *
      • This file is required if you do not use underscore in your project and you want to use floatThead.
      • It contains functions from underscore that the plugin uses. *
      • YOU DON'T NEED TO INCLUDE THIS IF YOU ALREADY INCLUDE UNDERSCORE! * */

    (function($){

    $.floatThead = $.floatThead || {};

    $.floatThead._ = window._ || (function(){ var that = {}; var hasOwnProperty = Object.prototype.hasOwnProperty, isThings = ['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp']; that.has = function(obj, key) { return hasOwnProperty.call(obj, key); }; that.keys = function(obj) { if (obj !== Object(obj)) throw new TypeError('Invalid object'); var keys = []; for (var key in obj) if (that.has(obj, key)) keys.push(key); return keys; }; var idCounter = 0; that.uniqueId = function(prefix) { var id = ++idCounter + ''; return prefix ? prefix + id : id; }; $.each(isThings, function(){ var name = this; that['is' + name] = function(obj) { return Object.prototype.toString.call(obj) == '[object ' + name + ']'; }; }); that.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; return function() { context = this; args = arguments; timestamp = new Date(); var later = function() { var last = (new Date()) - timestamp; if (last < wait) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) result = func.apply(context, args); } }; var callNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(later, wait); } if (callNow) result = func.apply(context, args); return result; }; }; return that; })(); })(jQuery);

    It worked for me JSfiddle. Here is the link for that: http://jsfiddle.net/833eLyod/18/

    need jsfiddle 
    opened by ghost 44
  • feedback can go here

    feedback can go here

    There is some stuff in here that I want to eventually do.

    simplify options object keys

    i cant remember what some of the options are called. make them simpler and bump major version

    Data attributes and plugin init

    I am going to add the ability to initialize the plugin via data attributes

    When using data attributes it would make sense to be able to init the plugin on when using overflow scrolling like this:

    <div style='overflow:auto; height:100px' data-provide='floatThead'>
       <table>.... </table>
    </div>
    

    which would be the equivalent to this (in the current version):

    $('table').floatThead(scrollContainer: function($table){ return $table.closest('div'); })
    

    except that I am thinking that it makes sense to allow you to run the floatThead plugin on non-table elements, and doing so will make the plugin assume that this is the scrollContainer element. It will then find the first table element within that DOM element and initialize it with overflow scrolling.

    Right now I throw an error if you run the plugin on non-table elements.

    The alternative would be to support this kind of syntax:

    <div style='overflow:auto; height:100px' id='my-container'>
       <table data-provide='floatThead' data-fth-scroll-container='#my-container'>....         </table>
    </div>
    

    Doing this will not introduce any 'breaking' changes but its not quite as nice.

    question 
    opened by mkoryak 27
  • Add to docs: Using with PrimeFaces

    Add to docs: Using with PrimeFaces

    Hey, it's me again. I said it'd work fine now, but... I stumbled upon the next issue and finally got a working jsfiddle for that. For the scenario: Got a table with responsive container, fixed width, fixed header which scrolls with the page. However, together with the fixed header, the table can not be resized under its current size anymore. See the following jsfiddle: http://jsfiddle.net/fYp44/47/ Making it bigger doesn't seem to be a problem. But under certain circumstances, I need to scale it down. Would be great if it'd be supported.

    Again, latest library version, Firefox 54.

    enhancement question 
    opened by Vyling 22
  • Body shrinking

    Body shrinking

    I'm seeing table's body shrink as soon as I apply floatThead. Here's the original state:

    screenshot 2015-12-28 12 54 04

    and here's what happens after $('table').floatThead():

    screenshot 2015-12-28 12 54 18

    I'm testing this on ES6 compat table. I've included the lib temporarily so you can see the outcome by running $('table').floatThead() in a console.

    Any idea what's going on?

    solved 
    opened by kangax 22
  • ScrollContainer assigned incorrect width to floatThead-container

    ScrollContainer assigned incorrect width to floatThead-container

    Follow the documentation to create an overflow scrolling table and set a fixed width to the table wrapper.

    • Google Chrome Version 57.0.2987.110 (64-bit)
    • Ubuntu 16.x
    • jQuery 3.2.1
    • floatThead latest via Bower

    https://jsfiddle.net/8wo3bprh/1/

    Table header is get cropped due to the inaccurate width and overflow:hidden.

    JSFiddle Screenshot,

    screenshot from 2017-03-23 16-10-06

    Production Screenshot,

    screenshot from 2017-03-23 15-53-11

    bug workaround 
    opened by ghost 20
  • jQuery is not defined when using with browserify

    jQuery is not defined when using with browserify

    I tried using the npm package inside my project that uses browserify and babelify and the script complains that jQuery is not defined, even though it IS imported and both defined as const jQuery in the file where floatThead is used, and in the window scope as window.jQuery and window.$ (required for bootstrap)

    I tried with import 'floatthead' and require('floatthead') but in both cases, the script complains that jQuery is not defined. Why not make a proper module?

    enhancement 
    opened by beeb 17
  • Floating header not aligning with table on window resize/shrink

    Floating header not aligning with table on window resize/shrink

    I've seen a similar issue reported (and fixed) in a few places. The symptom of the similar issue I've seen was that the last column was being hidden. My issue is different in that the problem is the floating header is not staying aligned with the table (see attached image) when the window is resized down. At max window size, everything works perfectly. Then, as the window shrinks, the table becomes more and more misaligned.

    I've tried reflow, debounce, useAbsolutePositioning = false, changed css in the inspector to see what that did. If it helps, I'm using Caspio, which may be part of the problem. I found instructions on implementing the floatTheader code in their datapages via their javascript solutions page: http://forums.caspio.com/index.php/topic/4678-j-freeze-header-in-a-tabular-report/

    Finally, I've also attached an image of the html code via the inspect element feature in Chrome. Seemed easier than typing everything out. Please let me know what else you need, e.g. the css, a fiddle, etc.

    Thanks!

    Screenshot of issue: image

    Screenshot of html: image

    opened by alex-silverman 17
  • Is it possible to set horizontal scroll to inner table?

    Is it possible to set horizontal scroll to inner table?

    Hi,

    First of all, thanks for such a great product!

    Sorry for off topic question, but I have the following scenario. I have a main div which is a kind of a workspace (which is not even in the top part of the screen). Inside that workspace, I have some divs and BELOW that I have a table. I am using floatThead and I set the scrollContainer: with the workspace id. This works correct in the sense that when I scroll down, the table header keeps fixed on the top part of the workspace area.

    The problem is that my table can have lots of tables, making it much wither than the stuff above (mostly controls related to the table) which is still inside the workspace.

    If I enable scrolling at the workspace level, then floatThead works correct in the sense I can scroll horizontally and header is aligned with body. However...what happens here is that for the user is very annoying because the upper part (controls) remain on the left. So...Ideally I would like to horizontally scroll in a table container so that I can only scroll the table and keep the above stuff visible.

    When I try to set a overflow-x: auto to my table container, the body parts scroll horizontally very well, but of course, the header (floating) does not scroll and keeps always in the same place with the old (already moved) table headers.

    I know it is a bit of a pain to explain without a jsfiddle but it is a bit complicated to isolated and get permissions from the client.

    So..in summary, is there something I can do so that I can set a bigger scrollContainer: (for the Y scroll) and then allow me to horizontally scroll my table with the header?

    Thanks in advance,

    question 
    opened by marianopeck 17
  • Fixed Positioning Floating Header to the Table, Then the Window

    Fixed Positioning Floating Header to the Table, Then the Window

    Greetings.

    I work for a company which exclusively uses IE (currently v.11), so I'm developing for that browser specifically.

    For simplicity's sake, I'll call table1 the original table ($table). I'll call the cloned/duplicated/copied thead table table2 (contains the thead content after being initialized).

    I've set the useAbsolutePositioning property to false. So, I'm relying on fixed positioning.

    Is there any way to dynamically switch between fixing table2 to the top of table1 and then fixing table2 to the top of the window?

    It seems in IE 11 that as table2 has to adjust to being a variable distance from the top of the window (I mean, the initial scrolling until the top of the window meets the top of the table), table2 noticeably shifts/skips/jumps until it has a top:0px; style.

    Is there any way to switch between the two points of reference (table1 and the window) for fixed positioning so that IE doesn't have to keep (noticeably) changing the top position style of table2?

    I hope I've worded that adequately well. Here's a fiddle: https://jsfiddle.net/p6SRV/106/ Thanks for your help.

    question 
    opened by niemeier23 16
  • Can't apply to multiple tables at once

    Can't apply to multiple tables at once

    I have multiple tables that all have the same class. Ideally, I'd like to do:

    $("table.ram").floatThead();
    

    and have it work on all the tables. Instead, it seems I need to call each one separately.

    need jsfiddle 
    opened by cdw9 16
  • Performance

    Performance

    I am experiencing inconsistent performance across browsers on larger tables (25 columns x 1000 rows).

    Chrome: 1s Firefox: 2.82s IE: 7s

    While this is certainly a great advertisement for chrome, my application requires consistent (and reasonable) performance across the browsers. The drag seems to be coming from getSizingRow() and sure enough chrome has an optimization in there. If I return $fthcols, the chrome path, for all browsers, I end up with;

    Chrome: 1s Firefox: 1.2s IE: 3.2s

    But of course - no surprise - does not render properly. Profiling suggests getting the CSS information on the nodes is taking the bulk of the time, up in jQuery.

    I will dig into the code to see what I can make of this issue, but perhaps you can point me in the right direction.

    question 
    opened by advaleigh 16
  • Firefox: Problem with RTL Tables

    Firefox: Problem with RTL Tables

    Hi, I know that: "RTL is not really supported by floatThead" floatThead works well on Chrome, and has no problems with RTL tables, But floatThead+RTL tables are not working properly on Firefox.

    Is there a way to fix the problem with Firefox?

    Please see the links below with Firefox And screen width smaller than 900 pixels: I've used the "Bootstrap table responsive". LTR: https://codepen.io/alex200/pen/XWWqLaQ RTL: https://codepen.io/alex200/pen/VwwxJWe

    bug 
    opened by alex2230 6
  • Table headers block scrollbar when the scroll container has padding

    Table headers block scrollbar when the scroll container has padding

    When the scroll container has left and/or right padding the width of the floatThead-container is not calculated correctly and the table headers are positioned over the scrollbar which makes the top of the scrollbar not usable.

    Steps to reproduce:

    • Add a div with left and right padding around the table and add the floatthead plugin on the table with the div as the scrollContainer.
    • Now try to use the top of the scrollbar by clicking on the top button of the scrollbar or dragging the scrollbar down from the top.

    JSFiddle: https://jsfiddle.net/rickhoutman/ptsh2y9L/4/

    bug workaround 
    opened by rickhoutman 6
  • Bounce effect when scrolling

    Bounce effect when scrolling

    No JSFiddle as the demo site seems adequate but I have provided a short video that hopefully demonstrates what I am seeing - https://cl.ly/lMlW

    When scrolling the translateY value of floatThead-container is updated and this lags slightly behind the scroll so the table head seems to 'bounce' in position as the user scrolls up and down.

    OS: macOS Browsers used:

    • Chrome 60
    • Firefox 55
    • Safari 10

    Is it possible to leave the floatThead-container static in the page until it becomes necessary to apply fixed (once the user has scrolled past)?

    enhancement workaround 
    opened by simonsmith 16
Releases(2.2.4)
  • 2.2.3(Aug 20, 2021)

  • 2.2.2(Aug 17, 2021)

  • v2.2.1(Jun 1, 2020)

  • 2.2.0(May 28, 2020)

  • 2.1.4(Aug 2, 2019)

  • 2.1.3(Jan 15, 2019)

    Maintenance release.

    bug fixes:

    #409 - inf recursion printing on FireFox #385 - bottom offset not respected in 'absolute' mode #303 - scrollContainer with other content inside wrapper, header won't scroll out #424 - table header fractional width set incorrectly.

    Source code(tar.gz)
    Source code(zip)
  • 2.1.2(Apr 16, 2018)

    bug fixes: #363 - scroll container assigned incorrect width because of a jquery 3.2.0 bug

    new features:

    • really really bad rtl support (only works with internal scrolling, not window scrolling)
    Source code(tar.gz)
    Source code(zip)
  • 2.1.1(Feb 21, 2018)

    I goofed, forgot to build dist last release.

    • Removed 'floatContainerOverflow' option, removed overflow-y:hidden css from scrollContainer.
    Source code(tar.gz)
    Source code(zip)
  • 2.1.0(Feb 21, 2018)

    NOTE: I forgot to publish dist/ for this release, so use 2.1.1

    • #402 HUGE performance improvement if plugin used on multiple tables on the same page. Over 10x faster startup time on a page with 39 tables. Big props to @ineuwirth for finding this one!

    On a side note, the last release was exactly one year ago. I didn't do this on purpose :)

    new features:

    • #404 - Adding a 'floatContainerOverflow' option to govern floatContainer CSS
    • #402 - performance improvement with many tables on the same page

    bug fixes:

    • #391 - Fix printing on firefox
    • #399 - Set header size on reflow
    • #361 - Works better on iPad Retina
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Jan 26, 2017)

    Breaking changes:

    • slim version no longer provided. Will use underscore, or shim the things we need
    • removed allowing deprecated options from 1.3.x
    • moved development version into /src dir and no longer use grunt here

    nothing big and exciting in version 2, just semvar and breaking changes.

    bug fixes:

    • #348 - headers Stay stuck after reposition
    • commonjs support works now, i promise
    Source code(tar.gz)
    Source code(zip)
  • 1.4.5(Nov 1, 2016)

    • #334 - fix memory leaks from print events
    • #335 - absolute position on overflow scrolling was broken if used with top option
    • added rudementary commonjs support to require jquery
    Source code(tar.gz)
    Source code(zip)
  • 1.4.4(Oct 3, 2016)

    #323 - use css transform (GPU) to float the header. (IE9+) #325 - fix bug introduced in 1.4.3 which caused issues on lots of resizing #327 - fix bug where reflowed event was being unbound after print

    Source code(tar.gz)
    Source code(zip)
  • 1.4.3(Aug 22, 2016)

    #316 - ie9 cant use matchmedia properly #321 - do not require Content Security Policy (CSP) style-src 'unsafe-inline'

    enableAria option has been removed. It is now always enabled.

    UPDATE: this has a bug that you want to avoid. Dont use.

    Source code(tar.gz)
    Source code(zip)
  • 1.4.2(Jul 10, 2016)

  • 1.4.1(May 13, 2016)

    • https://github.com/mkoryak/floatThead/issues/289 - properly handle overflow:scroll !important
    • https://github.com/mkoryak/floatThead/issues/298 - add ability to opt out of 3rd party lib integrations
    • https://github.com/mkoryak/floatThead/issues/303 - fix scrollContainer: true behavior
    • https://github.com/mkoryak/floatThead/issues/299 - fix weirdness when header cells contain tons of content
    • https://github.com/mkoryak/floatThead/issues/300 - remember scrollLeft of header when reflowing
    Source code(tar.gz)
    Source code(zip)
  • 1.3.1(Sep 23, 2015)

  • 1.3.0(Sep 21, 2015)

  • 1.2.13(Aug 19, 2015)

  • v1.2.12(May 27, 2015)

  • v1.2.11(Feb 20, 2015)

  • v1.2.10(Jan 5, 2015)

    • play nicely with angularjs if it modifies the DOM behind the scenes
    • screen reader support via enableAria option
    • https://github.com/mkoryak/floatThead/issues/122 - better default options for ie
    • https://github.com/mkoryak/floatThead/issues/121 - header layout bug
    • https://github.com/mkoryak/floatThead/issues/128 - issues with scrollbar size detection in certain layouts
    • https://github.com/mkoryak/floatThead/issues/127 - destroy not removing some elements
    Source code(tar.gz)
    Source code(zip)
  • v1.2.9(Oct 1, 2014)

    • Deprecated the cellTag option, use headerCellSelector instead (see docs)
    • https://github.com/mkoryak/floatThead/issues/101 - Huge performance improvement
    • https://github.com/mkoryak/floatThead/issues/98 - Border-collapse ignored on scroll
    • https://github.com/mkoryak/floatThead/issues/99 - Incorrect scroll width calculation in some cases
    • A couple of updates to the destroy method that get the table back into a more pristine state
    Source code(tar.gz)
    Source code(zip)
Owner
Misha Koryak
Riding the cat into the sunset
Misha Koryak
Live searching/filtering for HTML tables in a jQuery plugin

jQuery Filter Table Plugin This plugin will add a search filter to tables. When typing in the filter, any rows that do not contain the filter will be

Sunny Walker 175 Oct 29, 2022
jQuery plugin to make HTML tables responsive

FooTable V3 This is a complete re-write of the plugin. There is no upgrade path from V2 to V3 at present as the options and the way the code is writte

FooPlugins 2.1k Dec 19, 2022
jQuery plugin to export a html table to JSON, XML, CSV, TSV, TXT, SQL, Word, Excel, PNG and PDF

tableExport.jquery.plugin Export HTML Table to CSV DOC JSON PDF PNG SQL TSV TXT XLS (Excel 2000 HTML format) XLSX (Excel 2007 Office Open XML format)

null 918 Dec 29, 2022
null 136 Dec 30, 2022
Kuldeep 2 Jun 21, 2022
Turn your ES5 code into readable ES6. Lebab does the opposite of what Babel does.

Lebab Lebab transpiles your ES5 code to ES6/ES7. It does exactly the opposite of what Babel does. If you want to understand what Lebab exactly does, t

Lebab 5.5k Dec 31, 2022
A simple jQuery extension to make any HTML element sticky on scroll.

jquery.sticky.js A simple jQuery extension to make any HTML element sticky on scroll. Installation Just download the script and include it in your HTM

Achal Jain 2 Aug 22, 2022
Uncensorable, immutable microblogging platform that is completely decentralized and does not rely on any centralized systems.

Zooko Truly decentralized, immutable and uncensorable microblogging Zooko is a working-example, proof-of-concept proving that you can have a decentral

Publius Federalist 152 Apr 20, 2022
UI for Keploy platform that does API testing by dependency mocking without writing any test cases.

Welcome to Keploy UI ?? About Us Keploy is a no-code API testing platform. Keploy automatically generates unit test cases with dependency mocks from A

Keploy Inc 23 Dec 28, 2022
fixed-background-effect

Fixed Background Effect A simple template that takes advantage of the background-attachment CSS property to create a fixed background effect. Article

CodyHouse 50 Oct 28, 2022
Fast Map built for keys that are always fixed size uniformly distributed buffers.

turbo-hash-map Fast Map built for keys that are always fixed size uniformly distributed buffers. npm install turbo-hash-map Uses a prefix trie to map

Mathias Buus 39 Jun 20, 2022
Fixed Deposit sample app 📱

Fixed Deposit sample app ?? This is a sample Fixed Deposit app built using Setu Deposits. Mobile app: Built with React Native Server-side backend: Bui

Setu 17 Oct 22, 2022
A fixed-width file format toolset with streaming support and flexible options.

fixed-width A fixed-width file format toolset with streaming support and flexible options. Features Flexible: lot of options Zero dependencies: small

Evologi Srl 6 Jul 14, 2022
Framework for interacting with instagrams private api in a usable manner (forked from andre's work and improved and fixed)

node-ig-framework Framework for interacting with instagrams private api in a usable manner (forked from andre's work and improved and fixed) Installat

nine ecksen 3 Dec 31, 2022
Simple fixed background parallax effect in vanilla js.

Simple Parallax Simple fixed background parallax effect in vanilla js. See demo on Codepen. JS Initialise the plugin. The first argument identifies th

null 6 Jun 27, 2022
Javascript library for switching fixed elements on scroll through sections. Like Midnight.js, but without jQuery

Library for Switching Fixed Elements on Scroll Sometimes designers create complex logic and fix parts of the interface. Also they colour page sections

Vladimir Lysov 38 Sep 19, 2022
Fixed-point math for Bitcoin Script

bsv-fixmath Fixed-point implementations of exp, log, root and pow for Bitcoin SV. This repo contains two equivalent implementations, one in Typescript

Vala 5 Oct 18, 2022
The best Blooket hacks on the platform! These hacks are always working, all gamemode hacks work and will be fixed when broke.

Support Discord Server: https://discord.gg/UCHtVM4A Blooket Hack The Blooket Hack provided by Jude Why you should use this tool: Always working. When

Jude 26 Dec 20, 2022