dollarshaveclub / stickybits

Stickybits is a lightweight alternative to `position: sticky` polyfills 🍬
https://github.com/yowainwright/stickybits
MIT License
2.19k stars 167 forks source link

Solution for sticky table header #554

Closed LordPachelbel closed 5 years ago

LordPachelbel commented 5 years ago

I know the recommendation from the other issues about this is to not use this plugin on real tables, but I figured out a way that seems to work.

For non-IE browsers you use position: sticky on the <th> tags.

For IE, the table must have a <thead> and <tbody>, you invoke the plugin on the <thead> with the classes option enabled, and you use flexbox to space out the <th>s. The width of the <thead> has to be set, too, and if you want the table to be responsive then you need a window resize event handler to recalculate the width.

styles

(in Less syntax, but it's not as compact as it could be because I want to make things clear)

table.stickyheader {
  thead {
    // this only works in Firefox
    /*position: sticky;
    top: 0;
    z-index: 2;*/

    // but this works in Firefox, Chrome, and Edge
    th {
      position: sticky;
      top: 0;
      z-index: 2;
    }
  }
}

/* IE10+ styles */
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
  table.stickyheader {
    thead.js-is-sticky {
      z-index: 2;

      tr {
        display: flex;
        justify-content: space-between;

        th {
          display: block;
          flex: 1 1 auto;
          width: 100%;  // you can also set explicit widths for each column
        }
      }
    }
  }
}

jQuery

// in IE the <thead>'s width needs to be set in px values ("width: 100%;" won't work), so we'll get it from the <tbody>
function updateTableHeaderWidth() {
  $('table.stickyheader').each(function () {
    var tableWidth = $(this).find('tbody').css('width');
    $(this).find('thead').css('width', tableWidth);
  });
}

// call the function on page load and on window resize
updateTableHeaderWidth();
$(window).resize(function () {
  updateTableHeaderWidth();
});

// invoke the plugin on the <thead> with the `useStickyClasses` option set to `true`
$('table.stickyheader thead').stickybits({useStickyClasses: true});
LordPachelbel commented 5 years ago

compact version of the styles

table.stickyheader {
  thead {
    &.js-is-sticky {

      @media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
        z-index: 2;
        tr {
          display: flex;
          justify-content: space-between;

          th {
            display: block;
            flex: 1 1 auto;
            width: 100%;
          }
        }
      }
    }

    th {
      position: sticky;
      top: 0;
      z-index: 2;
    }
  }
}
yowainwright commented 5 years ago

Thanks for sharing, @LordPachelbel.