Thursday, July 10, 2014

Table header to stay fixed at the top when user scrolls using jQuery sticky plugin



jQuery sticky plugin  can be used to keep HTML table header to stay fixed at the top when user scrolls. jQuery sticky plugin can be downloaded from:
https://github.com/walmik/jquery.sticky
You need to copy jquery.sticky.min.js  and style.css to your working directory.
(Sometimes I found table header not align with body well when scroll down.
I slightly modify  jquery.sticky.js and directly use jquery.sticky.js, for example I found my table
header 10% less when I scrolled down, in this case in jquery.sticky.js
line 91 change
       $(firstRowThArr[i]).width(width);
to
         $(firstRowThArr[i]).width(Number(width)*1.1);

)
Example code:
<!DOCTYPE html>
<html>
<head>
    <title>jQuery Sticky plugin</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
  <table border="1" id="myTable">
    <tr><th>Problem</th><th>Solution</th> </tr>
   <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
   <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
   <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr>     
   <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
   <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
   <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr> 
    <tr><td>1</td><td>1</td> </tr>        
</table>
     <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
  <script  src="jquery.sticky.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        (function(){
            $('tr:even>td').addClass('alt');
            $("#myTable").sticky();
   //         $("#hdr3").sticky({bindWith: '#d1'});
    //        $("#hdr4").sticky();
        })();
    </script>       
  </body>
</html>

Note, if  you have menu <div id="menu"> </div> before the table, you may need set the position absolte/
       $("#menu").css('position', 'absolute');
style.css
body
{
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-size: 14px;
}

#wrapper
{
    width: inherit;
    margin: 0px auto;
}

table
{
    width: 100%;


}

th
{
    text-align: left;
    background-color: #333;
    color: #fff;
    padding: 5px;
}

td
{
    padding: 5px;
    background-color: #f2f2f2;
}

.alt { background-color: #ADD8E6; }

#hdr3, #hdr4{
    background: #333;
    color: #fff;
    padding: 10px;
    margin: 0px;
    width: 980px;
}

#d1 {
    background: #666;
    padding: 10px;
    color: #fff;
    width: 980px;
}


jquery.sticky.js
 (function($){

    var JQuerySticky = function(element, options) {
        var defaults = {
            isTable: true,
            bindWith: 'body'   //Default. Used when no element is specified as bindWidth along with a non-table element.
        };

        this.options = $.extend(defaults, options);
        this.$el = $(element);
       
        this.init();
    };

    /*
        Initialize the plugin with common properties
    */
    JQuerySticky.prototype.init = function() {

    //set layout properties of the element for later use
    this.$stickyEl = this.$el;    //the element on which sticky will be applied (changes in case of a table)
    this.elWidth = this.$el.width();
    this.elHeight = 0;        //required in case sticky is applied on a non table along with a bindWidth element to check for scroll
    this.pos = this.$el.offset();


        /*
      Sticky properties will change based on the type of element.
      In case of a table, the row with the table header elements will be sticky.
      In case of a non-table, the whole element will be sticky.
    */
        if(this.$el.prop('tagName').toLowerCase() != 'table') this.options.isTable = false;

    /*
      Change parameters based on the type of element (table or not)
    */
        if(this.options.isTable) this.initStickyOnTable();
        else this.initStickyOnElement();
   
    var self = this;

    /*
      Initialize sticky behavior on window.scroll
    */
    $(window).on('scroll', function(){
      var windowScrollTop = $(window).scrollTop();
      if(windowScrollTop > self.pos.top && windowScrollTop < self.pos.top + self.elHeight)
      {
        self.$stickyEl.css('position', 'fixed');
      }
      else
      {
        self.$stickyEl.css('position', 'static');
      }
    });
    };

    JQuerySticky.prototype.initStickyOnTable = function() {
        var self = this;

        //cache the first and last row coz we ll use em again in this scope
        var $firstRow = $(this.$el.find('tr:first'));
        var $lastRow = $(this.$el.find('tr:last'));

        //since this is a table, we ll use the table's height to monitor the sticky behavior on scroll
        //and subtract the thead row's height to subtract from elHeight
        this.elHeight = this.$el.height() - $firstRow.height();

    var firstRowThArr = $firstRow.find('th');
    var lastRowTdArr = $lastRow.find('td');

        //assign widths to the th and td elements so that they dont collapse during sticky
    this.$el.find("th").each(function(i, th){
      var width = $(th).width();
      $(firstRowThArr[i]).width(width);
      $(lastRowTdArr[i]).width(width);
    });

    //assign total width and left pos to the table header
    $firstRow.width(this.elWidth);
    $firstRow.css('top', '0px');
    $firstRow.css('left', this.pos.left + 'px');
    $lastRow.width(this.elWidth);

    /*
            On tables with border-collapse set to the default 'separate',
            a small spacing is seen on the top of the element when its sticky.
            To tackle this, get the Y value of that spacing and subtract it later during sticky.
    */
    //check border collapse to sort issues of a minor spacing on the top of the table when sicky
   
    if(this.$el.css('border-collapse') == 'separate') {

      var borderSpacingStr = this.$el.css('border-spacing'); //sample: 2px 2px
      //get the X and Y value from the sample
      var xBorderSpacing = borderSpacingStr.split(' ')[0];    //returns: 2px
      var yBorderSpacing = borderSpacingStr.split(' ')[1];    //returns: 2px
      //get rid of the 'px' and update margin top and left
      var marginTop = parseInt(yBorderSpacing.substring(0,yBorderSpacing.indexOf('px')), 10);
      $firstRow.css('top', (0 - marginTop) + 'px');
      //var marginLeft = this.pos.left + parseInt(xBorderSpacing.substring(0,xBorderSpacing.indexOf('px')), 10);
    }

    //Since this is a table, the table header row will be the sticky element
    this.$stickyEl = $firstRow;
   
    };

    JQuerySticky.prototype.initStickyOnElement = function() {
        var self = this;

    var marginTopStr = this.$el.css('margin-top'); //sample: 10px
    //get rid of the 'px'
    var marginTop = 0 - parseInt(marginTopStr.substring(0,marginTopStr.indexOf('px')), 10);
    this.$el.css('top',  marginTop +'px');
    this.$el.css('width', this.elWidth+'px');

    //init sticky on window scroll
    //bindWidth parameter uses 'body' as default, incase user specified an element, then use that
    this.elHeight = $(this.options.bindWith).height();
    };


    ///////////////////////////////////////////////////
    ///////////////INITIALIZE THE PLUGIN///////////////
    $.fn.sticky = function(menu) {
        return this.each(function() {
            new JQuerySticky(this, menu);
        });
    };
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////


 })(jQuery);

Video: Table header to stay fixed at the top when user scrolls using  jQuery sticky plugin

No comments:

Post a Comment