diff --git a/docs/example-widget-css-sticky-header.html b/docs/example-widget-css-sticky-header.html index 046d17e6..8cca3021 100644 --- a/docs/example-widget-css-sticky-header.html +++ b/docs/example-widget-css-sticky-header.html @@ -1,7 +1,7 @@ - + jQuery plugin: Tablesorter 2.0 - CSS Sticky Header Widget @@ -41,9 +41,8 @@ @@ -111,23 +118,55 @@ $(function() {

Notes

Change log

+
  • v2.14.1: Created css Sticky Headers widget
  • @@ -238,6 +277,136 @@ $(function() { + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Main table header
    Main table - row 1
    Main table - row 2
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Header for the nested table 1
    Second header row for nested table 1
    data in the nested table 1 - row 1
    data in the nested table 1 - row 2
    data in the nested table 1 - row 3
    data in the nested table 1 - row 4
    data in the nested table 1 - row 5
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Nested table 2 caption
    Header for the nested table 2
    Second header row for nested table 2
    data in the nested table 2 - row 1
    data in the nested table 2 - row 2
    data in the nested table 2 - row 3
    data in the nested table 2 - row 4
    data in the nested table 2 - row 5
    data in the nested table 2 - row 6
    data in the nested table 2 - row 7
    data in the nested table 2 - row 8
    data in the nested table 2 - row 9
    data in the nested table 2 - row 10
    data in the nested table 2 - row 11
    data in the nested table 2 - row 12
    data in the nested table 2 - row 13
    data in the nested table 2 - row 14
    data in the nested table 2 - row 15
    data in the nested table 2 - row 16
    data in the nested table 2 - row 17
    data in the nested table 2 - row 18
    data in the nested table 2 - row 19
    data in the nested table 2 - row 20
    +
    data in the nested table 1 - row 6
    data in the nested table 1 - row 7
    data in the nested table 1 - row 8
    data in the nested table 1 - row 9
    data in the nested table 1 - row 10
    data in the nested table 1 - row 11
    data in the nested table 1 - row 12
    data in the nested table 1 - row 13
    data in the nested table 1 - row 14
    data in the nested table 1 - row 15
    data in the nested table 1 - row 16
    data in the nested table 1 - row 17
    data in the nested table 1 - row 18
    data in the nested table 1 - row 19
    data in the nested table 1 - row 20
    +
    Main table - row 3
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Nested table 3 caption
    Header for the nested table 3
    data in the nested table 3 - row 1
    data in the nested table 3 - row 2
    data in the nested table 3 - row 3
    data in the nested table 3 - row 4
    data in the nested table 3 - row 5
    data in the nested table 3 - row 6
    data in the nested table 3 - row 7
    data in the nested table 3 - row 8
    data in the nested table 3 - row 9
    data in the nested table 3 - row 10
    data in the nested table 3 - row 11
    data in the nested table 3 - row 12
    data in the nested table 3 - row 13
    data in the nested table 3 - row 14
    data in the nested table 3 - row 15
    data in the nested table 3 - row 16
    data in the nested table 3 - row 17
    data in the nested table 3 - row 18
    data in the nested table 3 - row 19
    data in the nested table 3 - row 20
    +
    Main table - row 4
    +
    diff --git a/docs/example-widget-scroller.html b/docs/example-widget-scroller.html index 55a8adf8..5aed605c 100644 --- a/docs/example-widget-scroller.html +++ b/docs/example-widget-scroller.html @@ -65,7 +65,7 @@ $(function() { themes = 'default blue green grey ice black-ice dark dropbox', i, o = '', t = themes.split(' '); for (i = 0; i < t.length; i++) { - o += ''; + o += ''; } $('select') @@ -82,7 +82,7 @@ $(function() { }); $('table') .removeClass('tablesorter-' + t.join(' tablesorter-') + ' tablesorter-jui') - .addClass('tablesorter-' + theme.replace(/-/,'')); + .addClass('tablesorter-' + (theme === 'black-ice' ? 'blackice' : theme) ); // make sure columns align $(window).trigger('resize'); }).change(); diff --git a/docs/example-widget-sticky-header.html b/docs/example-widget-sticky-header.html index 01428910..c2caba04 100644 --- a/docs/example-widget-sticky-header.html +++ b/docs/example-widget-sticky-header.html @@ -1,21 +1,21 @@ - + jQuery plugin: Tablesorter 2.0 - Sticky Header Widget + + - - @@ -38,14 +38,23 @@ + + @@ -122,34 +144,203 @@ $(function() {
    -

    - NOTE! -

    -

    +

    +
    + +
    + +

    Notes

    +
    +
      +
    • In v2.18.0, +
        +
      • Nested tables with sticky headers now stack properly. See the new example added to the bottom of this demo page.
      • +
      • Added stickyHeaders_xScroll and stickyHeaders_yScroll widget options.
      • +
      • Any defined onRenderHeader function is now executed on the cloned sticky header table cells; the onRenderTemplate function is not! See the change log section for more details.
      • +
      +
      +
    • + +
    • Note To access the added sticky table content from your code without worrying about using the ID, you can use table.config.widgetOptions.$sticky.
    • +
    • Note Add the class name sticky-false to any header rows you don't want to become sticky (v2.1.18).

    • + +
    • You will need to modify the headerTemplate option to include the jQuery UI icon! See the example in the code (v2.7).
    • +
    • Scroll down the page to see the headers stick. Then sort the columns using the sticky headers!
    • +
    • Multiple rows in the header, including the filter row, will become sticky.
    • + +
    • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
    • +
    • Because of the limitations of Internet Explorer version 7 and older, this widget will not work.
    • + +
    +
    + +

    Options

    +
    +

    stickyHeaders widget defaults (added inside of tablesorter widgetOptions)

    +
    + TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionDefaultDescription
    ''Include any extra class name to be added to the sticky header table (v2.1; v2.18.0) +
    +
    + Note prior to v2.18.0, this class was applied to the sticky table thead; after v2.18.0, the thead and caption are wrapped in a div that is made sticky, with this class name.
    +
    + Modified in v2.11 so that "tablesorter-stickyHeader" class is always added and this option only adds additional classes. +
    +
    stickyHeaders_offset0Set this to a number (in pixels) or jquery selector targeting the position:fixed element in which to place the sticky header below while scrolling.
    '-sticky'If the original table has an ID, then the value from this option is added to the end of the cloned table to maintain a unique ID (v2.9). +
    +
      +
    • It contains a suffix to add to any table id.
    • +
    • Its default value is -sticky
    • +
    +
    +
    stickyHeaders_addResizeEventtrueWhen true, the $.tablesorter.addHeaderResizeEvent function is applied to the table header cells so a "resize" event is triggered and the sticky headers widget can then properly resize the cloned table header cells to match the originals.
    stickyHeaders_includeCaptiontrueIf false and a table caption exist, it won't be included in the sticky header.
    stickyHeaders_zIndex2Adjust the zIndex of the stickyHeaders element as desired.
    nullSet this option as a jQuery selector or object where the sticky header will be attached (v2.14.4). +
    +
      +
    • Setting this option with either a jQuery selector string (".wrapper") or jQuery object ($(".wrapper")).
    • +
    • This option contains the target to which the sticky header will attach - see the second example below.
    • +
    +
    +
    trueScroll table top into view after filtering (v2.16.2) +
    +
    + This is needed when the user choses to filter the table which results in fewer rows than are currently visible in the browser viewport. So, the sticky header may still exist, but the table body may not be seen. Setting this option to true forces the original table header to scroll back into view. +
    +
    nulljQuery selector or object that will be used to monitor horizontal scroll position(v2.18.0) +
    +
    + Defaults: xScroll > attachTo > window
    +
    + Indicate the element (jQuery selector or obect) in which to monitor for changes in scroll position; If undefined (null by default), the window is monitored. +
    +
    nulljQuery selector or object that will be used to monitor vertical scroll position (v2.18.0) +
    +
    + Defaults: yScroll > attachTo > window
    +
    + Indicate the element (jQuery selector or obect) in which to monitor for changes in scroll position; If undefined (null by default), the window is monitored. +
    +
    +
    + +

    Change log

    +
    +
      +
    • v2.18.0: +
        +
      • Nested tables with sticky headers now stack properly. See the new example added to the bottom of this demo page.
      • +
      • Added stickyHeaders_xScroll and stickyHeaders_yScroll options to indicate the element (jQuery selector or obect) in which to monitor for changes in scroll position; If undefined (null by default), the window is monitored.
      • +
      • Any defined onRenderHeader function is now executed on the cloned sticky header table cells. There are now 3 parameters available to this function: +
          +
        • index - header cell index; this is not the column index!
        • +
        • config - the table.config settings for the table.
        • +
        • $table - the target of this parameter changes: +
            +
          • For the original table header cells, this parameter is a jQuery object pointing to the original table; this table has the class name hasStickyHeaders.
          • +
          • When the sticky table header cells are processed, this parameter is a jQuery object pointing to a copy of the table that is used as a sticky header; this table has the class name containsStickyHeaders.
          • +
          +
        • +
        + Note Any defined onRenderTemplate function is not called on the sticky header because it is a clone of the already rendered header cell; instead use onRenderHeader to attach event listeners, etc. +
      • +
      • Removed jQuery UI theme from the theme selector in this demo to accomodate adding the accordion. With the current setup, changing the table theme would continue to show the jQuery UI class names on the table (uitheme is not removed when switching themes), so the tables became a chimera of jQuery UI and the selected theme, and the accordion would become un-styled; it was a real mess.
        +
        +
      • +
      +
    • +
    • v2.14.4: Added cssStickyHeaders_attachTo option (default set to null). +
        +
      • Setting this option with either a jQuery selector string (".wrapper") or jQuery object ($(".wrapper")).
      • +
      • This option contains the target to which the sticky header will attach - see the second example below.
      • +
      +
    • +
    • v2.9: +
        +
      • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
      • +
      • Added a widget option named stickyHeaders_cloneId +
          +
        • It contains a suffix to add to any table id.
        • +
        • Its default value is -sticky
        • +
        +
      • +
      • Table captions and any additional rows (filter widget row) will also be included in the sticky header.
      • +
      +
    • +
    • v2.7: You will need to modify the headerTemplate option to include the jQuery UI icon! See the example in the code.
    • +
    • v2.1.18: Add the class name sticky-false to any header rows you don't want to become sticky.
    • +
    • v2.1.17: Multiple rows in the header will become sticky.
    • +
    • v2.1 (updated v2.11): Added a widget option named stickyHeaders option which contains the css class name applied to the actual sticky header. Modified in v2.11 so that "tablesorter-stickyHeader" class is always added and this option only adds additional classes.
    • +
    +
    + +

    CSS

    @@ -163,13 +354,11 @@ $(function() {

    Demo

    Choose Theme: - +

    - + @@ -233,7 +422,7 @@ $(function() {
    Student GradesStudent Grades
    NameMajorSexEnglishJapaneseCalculusGeometry
    - + @@ -257,6 +446,135 @@ $(function() {
    Student GradesStudent Grades
    Account #First NameLast NameAgeTotalDiscountDiff
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Main table header
    Main table - row 1
    Main table - row 2
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Header for the nested table 1
    Second header row for nested table 1
    data in the nested table 1 - row 1
    data in the nested table 1 - row 2
    data in the nested table 1 - row 3
    data in the nested table 1 - row 4
    data in the nested table 1 - row 5
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Nested table 2 caption
    Header for the nested table 2
    Second header row for nested table 2
    data in the nested table 2 - row 1
    data in the nested table 2 - row 2
    data in the nested table 2 - row 3
    data in the nested table 2 - row 4
    data in the nested table 2 - row 5
    data in the nested table 2 - row 6
    data in the nested table 2 - row 7
    data in the nested table 2 - row 8
    data in the nested table 2 - row 9
    data in the nested table 2 - row 10
    data in the nested table 2 - row 11
    data in the nested table 2 - row 12
    data in the nested table 2 - row 13
    data in the nested table 2 - row 14
    data in the nested table 2 - row 15
    data in the nested table 2 - row 16
    data in the nested table 2 - row 17
    data in the nested table 2 - row 18
    data in the nested table 2 - row 19
    data in the nested table 2 - row 20
    +
    data in the nested table 1 - row 6
    data in the nested table 1 - row 7
    data in the nested table 1 - row 8
    data in the nested table 1 - row 9
    data in the nested table 1 - row 10
    data in the nested table 1 - row 11
    data in the nested table 1 - row 12
    data in the nested table 1 - row 13
    data in the nested table 1 - row 14
    data in the nested table 1 - row 15
    data in the nested table 1 - row 16
    data in the nested table 1 - row 17
    data in the nested table 1 - row 18
    data in the nested table 1 - row 19
    data in the nested table 1 - row 20
    +
    Main table - row 3
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Nested table 3 caption
    Header for the nested table 3
    data in the nested table 3 - row 1
    data in the nested table 3 - row 2
    data in the nested table 3 - row 3
    data in the nested table 3 - row 4
    data in the nested table 3 - row 5
    data in the nested table 3 - row 6
    data in the nested table 3 - row 7
    data in the nested table 3 - row 8
    data in the nested table 3 - row 9
    data in the nested table 3 - row 10
    data in the nested table 3 - row 11
    data in the nested table 3 - row 12
    data in the nested table 3 - row 13
    data in the nested table 3 - row 14
    data in the nested table 3 - row 15
    data in the nested table 3 - row 16
    data in the nested table 3 - row 17
    data in the nested table 3 - row 18
    data in the nested table 3 - row 19
    data in the nested table 3 - row 20
    +
    Main table - row 4
    +

    diff --git a/docs/index.html b/docs/index.html index 8d356be2..104a57db 100644 --- a/docs/index.html +++ b/docs/index.html @@ -510,9 +510,9 @@
  • Save sort widget (v2.0.27)
  • Scroller widget (v2.9; v2.17.3).
  • Beta StaticRow widget (v2.16; v2.17.3).
  • -
  • Sticky header widget (v2.0.21.1; v2.16.2)
  • -
  • Sticky header (css3) widget (v2.14.2; v2.16.4).
  • -
  • UITheme widget (v2.17.4): +
  • Sticky header widget (v2.0.21.1; v2.18.0)
  • +
  • Sticky header (css3) widget (v2.14.2; v2.18.0).
  • +
  • UITheme widget (v2.17.4; v2.18.0):
    • jQuery UI theme (v2.0.9)
    • Bootstrap (v2.4)
    • @@ -1413,7 +1413,7 @@ From the example function above, you'll end up with something similar to this HT String "default" - This option will add a theme css class name to the table "tablesorter-{theme}" for styling (v2.4; v2.16.4). + This option will add a theme css class name to the table "tablesorter-{theme}" for styling (v2.4; v2.18.0).

      When changing this theme option, make sure that the appropriate css theme file has also been loaded. Included theme files include: see all themes
      @@ -1724,7 +1724,11 @@ $(function(){ // extra class name added to the sticky header row stickyHeaders : '', // jQuery selector or object to attach sticky header to - stickyHeaders_attachTo : null + stickyHeaders_attachTo : null, + // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_xScroll : null, + // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_yScroll : null, // number or jquery selector targeting the position:fixed element stickyHeaders_offset : 0, // scroll table top into view after filtering @@ -1736,7 +1740,7 @@ $(function(){ // if false and a caption exist, it won't be included in the sticky header stickyHeaders_includeCaption : true, // The zIndex of the stickyHeaders, allows the user to adjust this to their needs - stickyHeaders_zIndex : 2 + stickyHeaders_zIndex : 2, // *** resizable widget *** // if false, resized columns are not saved for next page reload @@ -2971,6 +2975,54 @@ $('table').trigger('search', false);
      Example + + + String + null + + Sticky Headers widget: points to the element in which to monitor for horizontal scrolling (v2.18.0). +
      +
      + If undefined (or null), the window element will be monitored.
      +
      + Use the stickyHeaders_xScroll option as follows:
      +
      $(function(){
      +  $("table").tablesorter({
      +    widgets: ["stickyHeaders"],
      +    widgetOptions : {
      +      // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window)
      +      stickyHeaders_xScroll : '.wrapper' // $('.wrapper') jQuery object can also be used
      +    }
      +  });
      +});
      + + Example + + + + + String + null + + Sticky Headers widget: points to the element in which to monitor for vertical scrolling (v2.18.0). +
      +
      + If undefined (or null), the window element will be monitored.
      +
      + Use the stickyHeaders_yScroll option as follows:
      +
      $(function(){
      +  $("table").tablesorter({
      +    widgets: ["stickyHeaders"],
      +    widgetOptions : {
      +      // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window)
      +      stickyHeaders_yScroll : '.wrapper' // $('.wrapper') jQuery object can also be used
      +    }
      +  });
      +});
      + + Example + + Multiple diff --git a/js/jquery.tablesorter.widgets.js b/js/jquery.tablesorter.widgets.js index c7588c3a..0e897006 100644 --- a/js/jquery.tablesorter.widgets.js +++ b/js/jquery.tablesorter.widgets.js @@ -9,8 +9,8 @@ * [ "columns", "filter", "resizable", "stickyHeaders", "uitheme", "saveSort" ] */ /*jshint browser:true, jquery:true, unused:false, loopfunc:true */ -/*global jQuery: false, localStorage: false, navigator: false */ -;(function($) { +/*global jQuery: false, localStorage: false */ +;(function ($, window) { "use strict"; var ts = $.tablesorter = $.tablesorter || {}; @@ -55,7 +55,8 @@ $.extend(ts.css, { wrapper : 'tablesorter-wrapper', // ui theme & resizable resizer : 'tablesorter-resizer', // resizable sticky : 'tablesorter-stickyHeader', // stickyHeader - stickyVis : 'tablesorter-sticky-visible' + stickyVis : 'tablesorter-sticky-visible', + stickyWrap: 'tablesorter-sticky-wrapper' }); // *** Store data in local storage, with a cookie fallback *** @@ -1536,6 +1537,8 @@ ts.addWidget({ options: { stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element stickyHeaders_filteredToTop: true, // scroll table top into view after filtering stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists @@ -1549,54 +1552,74 @@ ts.addWidget({ return; } var $table = c.$table, - $attach = $(wo.stickyHeaders_attachTo || 'window'), + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), $thead = $table.children('thead:first'), - $win = $attach.length ? $attach : $(window), $header = $thead.children('tr').not('.sticky-false').children(), - innerHeader = '.' + ts.css.headerIn, - $tfoot = $table.find('tfoot'), + $tfoot = $table.children('tfoot'), $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', stickyOffset = $attach.length ? 0 : $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders') - .css({ - position : $attach.length ? 'absolute' : 'fixed', - margin : 0, - top : stickyOffset, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2 - }), - $stickyThead = $stickyTable.children('thead:first').addClass(ts.css.sticky + ' ' + wo.stickyHeaders), + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) + .wrap('
      '), + $stickyWrap = $stickyTable.parent().css({ + position : $attach.length ? 'absolute' : 'fixed', + margin : 0, + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', spacing = 0, - nonwkie = $table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent), + setWidth = function($orig, $clone){ + $orig.filter(':visible').each(function(i) { + var width, border, + $cell = $clone.filter(':visible').eq(i), + $this = $(this); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle(this, null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } + } else { + width = $this.width(); + } + } + $cell.css({ + 'min-width': width, + 'max-width': width + }); + }); + }, resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; spacing = 0; - // yes, I dislike browser sniffing, but it really is needed here :( - // webkit automatically compensates for border spacing - if (nonwkie) { - // Firefox & Opera use the border-spacing - // update border-spacing here because of demos that switch themes - spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; - } - $stickyTable.css({ - left : $attach.length ? (parseInt($attach.css('padding-left'), 10) || 0) + parseInt(c.$table.css('padding-left'), 10) + - parseInt(c.$table.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : - $thead.offset().left - $win.scrollLeft() - spacing, - width: $table.width() - }); - $stickyCells.filter(':visible').each(function(i) { - var $cell = $header.filter(':visible').eq(i), - // some wibbly-wobbly... timey-wimey... stuff, to make columns line up in Firefox - offset = nonwkie && $(this).attr('data-column') === ( '' + parseInt(c.columns/2, 10) ) ? 1 : 0; - $(this) - .css({ width: $cell.width() - spacing }) - .find(innerHeader).width( $cell.find(innerHeader).width() - offset ); + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() }); + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); }; // fix clone ID, if it exists - fixes #271 if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } @@ -1606,42 +1629,60 @@ ts.addWidget({ $stickyTable.find('tbody, tfoot').remove(); if (!wo.stickyHeaders_includeCaption) { $stickyTable.find('caption').remove(); - } else { - $stickyTable.find('caption').css( 'margin-left', '-1px' ); } // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); - $stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 }); + $stickyTable.css({ height:0, width:0, margin: 0 }); // remove resizable block $stickyCells.find('.' + ts.css.resizer).remove(); // update sticky header class names to match real header after sorting $table .addClass('hasStickyHeaders') - .bind('pagerComplete.tsSticky', function() { + .bind('pagerComplete' + namespace, function() { resizeHeader(); }); ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header')); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyTable ); + $table.after( $stickyWrap ); + + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $stickyThead.children('tr').children().each(function(index){ + // send second parameter + c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); + }); + } + // make it sticky! - $win.bind('scroll.tsSticky resize.tsSticky', function(event) { + $xScroll.add($yScroll) + .unbind('scroll resize '.split(' ').join( namespace ) ) + .bind('scroll resize '.split(' ').join( namespace ), function(event) { if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; var prefix = 'tablesorter-sticky-', offset = $table.offset(), - captionHeight = (wo.stickyHeaders_includeCaption ? 0 : $table.find('caption').outerHeight(true)), - scrollTop = ($attach.length ? $attach.offset().top : $win.scrollTop()) + stickyOffset - captionHeight, - tableHeight = $table.height() - ($stickyTable.height() + ($tfoot.height() || 0)), - isVisible = (scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden', + yWindow = $.isWindow( $yScroll[0] ), + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden', cssSettings = { visibility : isVisible }; + if ($attach.length) { - cssSettings.top = $attach.scrollTop(); - } else { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $thead.offset().left - $win.scrollLeft() - spacing; + cssSettings.top = yWindow ? scrollTop : $attach.scrollTop(); } - $stickyTable + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap .removeClass(prefix + 'visible ' + prefix + 'hidden') .addClass(prefix + isVisible) .css(cssSettings); @@ -1656,14 +1697,14 @@ ts.addWidget({ } // look for filter widget - if ($table.hasClass('hasFilters')) { + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { // scroll table into view after filtering, if sticky header is active - #482 - $table.bind('filterEnd', function() { + $table.bind('filterEnd' + namespace, function() { // $(':focus') needs jQuery 1.6+ var $td = $(document.activeElement).closest('td'), column = $td.parent().children().index($td); // only scroll if sticky header is active - if ($stickyTable.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { // scroll to original table (not sticky clone) window.scrollTo(0, $table.position().top); // give same input/select focus; check if c.$filters exists; fixes #594 @@ -1683,14 +1724,16 @@ ts.addWidget({ }, remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind('pagerComplete.tsSticky') - .find('.' + ts.css.sticky).remove(); + .unbind( 'pagerComplete filterEnd '.split(' ').join(namespace) ) + .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table // don't unbind if any table on the page still has stickyheaders applied if (!$('.hasStickyHeaders').length) { - $(window).unbind('scroll.tsSticky resize.tsSticky'); + $(window).add(wo.stickyHeaders_xScroll).add(wo.stickyHeaders_yScroll).add(wo.stickyHeaders_attachTo) + .unbind( 'scroll resize '.split(' ').join(namespace) ); } ts.addHeaderResizeEvent(table, false); } @@ -1925,4 +1968,4 @@ ts.addWidget({ } }); -})(jQuery); +})(jQuery, window); diff --git a/js/widgets/widget-cssStickyHeaders.js b/js/widgets/widget-cssStickyHeaders.js index 27f5f510..e7cc0d83 100644 --- a/js/widgets/widget-cssStickyHeaders.js +++ b/js/widgets/widget-cssStickyHeaders.js @@ -3,66 +3,93 @@ */ /*jshint jquery:true, unused:false */ ;(function($){ - "use strict"; + 'use strict'; - $.tablesorter.addWidget({ - id: "cssStickyHeaders", + var ts = $.tablesorter; + + ts.addWidget({ + id: 'cssStickyHeaders', priority: 10, options: { cssStickyHeaders_offset : 0, cssStickyHeaders_addCaption : false, + // jQuery selector or object to attach sticky header to cssStickyHeaders_attachTo : null, - cssStickyHeaders_filteredToTop : true, - cssStickyHeaders_zIndex : 10 + cssStickyHeaders_filteredToTop : true }, init : function(table, thisWidget, c, wo) { - var $attach = $(wo.cssStickyHeaders_attachTo), - namespace = '.cssstickyheader', - $thead = c.$table.children('thead'), - $caption = c.$table.find('caption'), - $win = $attach.length ? $attach : $(window); - $win.bind('scroll resize '.split(' ').join(namespace + ' '), function() { + var isIE = 'ActiveXObject' in window, // target all versions of IE + $table = c.$table, + $attach = $(wo.cssStickyHeaders_attachTo), + namespace = c.namespace + 'cssstickyheader ', + $thead = $table.children('thead'), + $caption = $table.children('caption'), + $win = $attach.length ? $attach : $(window), + $parent = $table.parent().closest('table.' + ts.css.table), + $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : []; + + $win + .unbind('scroll resize '.split(' ').join(namespace)) + .bind('scroll resize '.split(' ').join(namespace), function() { var top = $attach.length ? $attach.offset().top : $win.scrollTop(), // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) // border-spacing needed in Firefox, but not webkit... not sure if I should account for that - captionTop = wo.cssStickyHeaders_addCaption ? $caption.outerHeight(true) + - (parseInt(c.$table.css('padding-top'), 10) || 0) + (parseInt(c.$table.css('border-spacing'), 10) || 0) : 0, - bottom = c.$table.height() - $thead.height() - (c.$table.find('tfoot').height() || 0) - captionTop, - deltaY = top - $thead.offset().top + (parseInt(c.$table.css('border-top-width'), 10) || 0) + - (wo.cssStickyHeaders_offset || 0) + captionTop, - finalY = (deltaY > 0 && deltaY <= bottom ? deltaY : 0), - // IE can only transform header cells - fixes #447 thanks to @gakreol! - $cells = $thead.children().children(); + captionHeight = wo.cssStickyHeaders_addCaption ? ( $caption.outerHeight(true) || 0 ) + + ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ) : 0, + + bottom = $table.height() - $thead.height() - ( $table.children('tfoot').height() || 0 ) - captionHeight, + // get bottom of nested sticky headers + nestedStickyTop = $parentThead.length ? ( isIE ? $parent.data('cssStickyHeaderTop') : $parentThead.offset().top ) + + $parentThead.height() - $win.scrollTop() : 0, + + // Detect nested tables - fixes #724 + deltaY = top - $table.offset().top + nestedStickyTop + ( parseInt( $table.css('border-top-width'), 10 ) || 0 ) + + // Again, I dislike browser sniffing... but I have no idea why I need to include a captionHeight + // for Firefox here and not for Chrome. Even IE behaves, sorta! + ( wo.cssStickyHeaders_offset || 0 ) + ( navigator.userAgent.toLowerCase().indexOf('firefox') > -1 ? captionHeight : 0 ), + + finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0, + + // All IE (even IE11) can only transform header cells - fixes #447 thanks to @gakreol! + $cells = isIE ? $thead.children().children() : $thead; + + // more crazy IE stuff.. somehow the second nested table is completely ignored + if (isIE) { + c.$table.data('cssStickyHeaderTop', finalY - ( $parentThead.length ? $parentThead.height() : 0 )); + if ($parentThead.length) { + top = $parent.data('cssStickyHeaderTop') - $parentThead.height(); + finalY = top > 0 && top <= bottom ? top : 0; + } + } + if (wo.cssStickyHeaders_addCaption) { $cells = $cells.add($caption); } + $cells.css({ - "position" : "relative", - "z-index" : wo.cssStickyHeaders_zIndex, - "transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)", - "-ms-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)", - "-webkit-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)" + 'transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', + '-ms-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', + '-webkit-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)' }); }); - c.$table.bind('filterEnd', function() { + $table.unbind('filterEnd' + namespace).bind('filterEnd' + namespace, function() { if (wo.cssStickyHeaders_filteredToTop) { // scroll top of table into view - window.scrollTo(0, c.$table.position().top); + window.scrollTo(0, $table.position().top); } }); }, remove: function(table, c, wo){ - var namespace = '.cssstickyheader'; - $(window).unbind('scroll resize '.split(' ').join(namespace + ' ')); + var namespace = c.namespace + 'cssstickyheader '; + $(window).unbind('scroll resize '.split(' ').join(namespace)); c.$table - .unbind('update updateAll '.split(' ').join(namespace + ' ')) + .unbind('filterEnd scroll resize '.split(' ').join(namespace)) + .add( c.$table.children('thead').children().children() ) .children('thead, caption').css({ - "position" : "", - "z-index" : "", - "transform" : "", - "-ms-transform" : "", - "-webkit-transform" : "" + 'transform' : '', + '-ms-transform' : '', + '-webkit-transform' : '' }); } });