MediaWiki:StickyTableHeaders.js

From Ephinea PSO Wiki
Revision as of 03:09, 5 November 2017 by Matt (talk | contribs) (Undo revision 4243 by Matt (talk))

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/******************************************************
 * Usage:                                             *
 *   - Add the `sticky` class to the table element.   *
 *   - Add the `sticky-main` class to a header row    *
 *     to define it as the main header.               *
 *   - Optionally, add the `sticky-sub` class to any  *
 *     header rows to define them as sub-headers.     *
 *                                                    *
 * Example:                                           *
 *   {| class="wikitable sticky"                      *
 *   |- class="sticky-main"                           *
 *   ! Name                                           *
 *   ! Color                                          *
 *   ! Score                                          *
 *                                                    *
 *   |- class="sticky-sub"                            *
 *   ! colspan=3 | Fruits                             *
 *   |-                                               *
 *   | Banana || Yellow || 10                         *
 *   |-                                               *
 *   | Pear || Green || 5                             *
 *   |-                                               *
 *   | Apple || Red || 7                              *
 *                                                    *
 *   |- class="sticky-sub"                            *
 *   ! colspan=3 | Vegetables                         *
 *   |-                                               *
 *   | Carrot || Orange || 8                          *
 *   |-                                               *
 *   | Potato || Brown || 6                           *
 *   |-                                               *
 *   | Eggplant || Purple || 10                       *
 *                                                    *
 *   |- class="sticky-sub"                            *
 *   ! colspan=3 | Candy                              *
 *   |-                                               *
 *   | Skittles || Rainbow || 4                       *
 *   |-                                               *
 *   | Chocolate || Brown || 7                        *
 *   |-                                               *
 *   | Liquorice || Black || 8                        *
 *   |}                                               *
 *                                                    *
 *****************************************************/

(function(){
  var $window = $(window);
  var borderWidth = 2;

  /**/

  function StickyTable(tableElement){
    this.tableElement = tableElement;

    this.mainHeader = $(".sticky-main", this.tableElement).first();
    if(this.mainHeader.length === 0){
      throw new TypeError("Invalid sticky table: Must contain a row with class `sticky-main`.");
    }

    this.mainHeaderCells = this.mainHeader.children().map(function(){
      return $(this);
    });

    this.subHeaders = $(".sticky-sub", this.tableElement).map(function(){
      return $(this);
    });
    this.subHeaderCells = [];
    for(var i = 0; i < this.subHeaders.length; i++){
      this.subHeaderCells[i] = this.subHeaders[i].children().map(function(){
        return $(this);
      });
    }
  }
  StickyTable.prototype.updateMain = function updateMain(scrollTop){
    var tableHeight = this.tableElement.height();
    var delta = scrollTop - this.mainHeader.offset().top;
    var y = (delta < 0 || delta > tableHeight) ? 0 : delta - borderWidth;
    var transformStyle = y === 0 ? "unset" : "translateY(" + y + "px)";

    for(var i = 0; i < this.mainHeaderCells.length; i++){
      this.mainHeaderCells[i].css("transform", y === 0 ? "unset" : "translateY(" + y + "px)");
    }
  }
  StickyTable.prototype.updateSubs = function updateSub(scrollTop){
    var mainHeight = this.mainHeader.height() - borderWidth;
    var tableTop = this.tableElement.offset().top;

    for(var i = 0; i < this.subHeaders.length; i++){
      var subHeader = this.subHeaders[i];
      var subTop = subHeader.offset().top;
      var tableBottom = this.tableElement.height() - subTop + tableTop;
      var delta = scrollTop - subTop + mainHeight;
      var y = (delta < 0 || delta > tableBottom) ? 0 : delta - borderWidth;
      var transformStyle = y === 0 ? "unset" : "translateY(" + y + "px)";

      for(var j = 0; j < this.subHeaderCells[i].length; j++){
        this.subHeaderCells[i][j].css("transform", transformStyle);
      }
    }
  }
  StickyTable.prototype.update = function update(scrollTop){
    this.updateMain(scrollTop);
    this.updateSubs(scrollTop);
  }

  /**/

  var tables = $("table.sticky").map(function(){
    return new StickyTable($(this));
  });
  var updating = false;

  function updateTables(){
    var scrollTop = $window.scrollTop()
    for(var i = 0; i < tables.length; i++){
      var table = tables[i];
      if(table.tableElement.is(":visible")) table.update(scrollTop);
    }
    updating = false;
  }

  function updateTablesThrottle(){
    if(!updating){
      window.requestAnimationFrame(updateTables);
      updating = true;
    }
  }

  if(tables.length > 0) $window.on("scroll", updateTablesThrottle);
})();