4

I'm trying to keep a table header fixed using the Javascript scroll event to manipulate the table header's top property.

This technique seems to perform drastically different depending on browser and screen resolution (My primary site visitors are Retina MBP). Currently it stutters badly. It may seem to work fine in this Fiddle, but will actually be slow and janky once on your desktop.

https://jsfiddle.net/taylorpalmer/exp057a5/

I need to be able to scroll the page and have the table header stick as you scroll past it.

          var allTh = document.querySelectorAll("th");
          var leftCells = document.querySelectorAll(".fixed-col");
          var latestKnownScrollX = 0;
          var latestKnownScrollY = 0;
          var ticking = false;


          var onScroll = function() {
            latestKnownScrollX = document.body.scrollLeft;
            latestKnownScrollY = document.body.scrollTop;
            requestTick();
          }

          function requestTick() {
            if (!ticking) {
              requestAnimationFrame(update);
            }
            ticking = true;
          }

          var immediate = true;

          var update = function() {
            console.log('scrolling');
            ticking = false;
            var currentScrollY = latestKnownScrollY;
            var currentScrollX = latestKnownScrollX;

            var translateHead = (currentScrollY) +"px"; 

            for(var i=0; i < allTh.length; i++ ) {
              allTh[i].style.top = translateHead;
            }

            var translateCell = currentScrollX + "px";

            for(var i=0; i < leftCells.length; i++ ) {
              leftCells[i].style.left = translateCell;
            }

          };

        window.addEventListener("scroll", onScroll);

Things I have already tried:

  • Using requestAnimationFrame() – it's currently implemented
  • Debouncing scroll – didn't improve performance
  • Throttling scroll - didn't improve performance
  • Using transform: translate() instead of top – didn't make a difference

Things I've thought about, but won't work

  • Using position: fixed or similar: the header cells would lose their dynamic widths and make the table worthless
taylorpalmer
  • 117
  • 9
  • I've been able to produce better scrolling performance using `position: fixed` and then manipulating the opposite axis with the `scroll` event, but it's twice as much work. Any ideas to improve normal scroll monitoring? – taylorpalmer Oct 27 '16 at 14:02

2 Answers2

0

How about doing like this?

https://jsfiddle.net/keikei/g3mt0rq6/

It's inspired by this answer of the question

js:

var tableOffset = $("#table-1").offset().top;
var $header = $("#table-1 > thead").clone();
var $row = $("#table-1 > tbody > tr").first().clone();
var $fixedHeader = $("#header-fixed")
  .append($header)
  .append($("<tbody>").append($row));

$(window).bind("scroll", function() {
  var offset = $(this).scrollTop();

  if (offset >= tableOffset && $fixedHeader.is(":hidden")) {
    $fixedHeader.show();
  } else if (offset < tableOffset) {
    $fixedHeader.hide();
  }
});

html:

<body>
  <div id="wrap">
    <table id="header-fixed"></table>
    <table id="table-1">
      <thead>
        <tr>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
          <th>Test</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="fixed-col">Test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test<span style="position: relative">Relative</span></td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
          <td>test</td>
        </tr>
        <!-- and so on... --->
      </tbody>
    </table>
  </div>
</body>
<script>


</script>

css:

#header-fixed {
  position: fixed;
  top: 0px;
  display: none;
  z-index: 20;
}

#header-fixed > tbody {
  visibility: hidden;
}

#header-fixed > tbody > tr > td {
  height: 0;
  font-size: 0;
  border: none;
}

th {
  height: 100px;
  background: #999;
  color: white;
  position: relative;
  z-index: 10;
}

td {
  background-color: white;
  min-width: 100px;
  height: 100px;
  text-align: center;
  position: relative;
  z-index: 1;
}

.fixed-col {
  z-index: 5;
}

body,
html {
  margin: 0;
}

thead {
  position: relative;
  z-index: 6;
}
Community
  • 1
  • 1
Kei
  • 26
  • 3
  • Thanks! I've seen this approach before, but there are couple of issues with your implementation: 1. The header doesn't scroll right to left with the table and 2. The first column isn't fixed. – taylorpalmer Oct 27 '16 at 03:59
0

Final solution: Javascript scrolling just can't compete with position: fixed for performance. I ended up using position: fixed and then manipulating the top and left properties as necessary to complete my table.

taylorpalmer
  • 117
  • 9