question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Fixed grid headers with CSS position sticky

See original GitHub issue

Hi @bvaughn,

I’m sure you’re really tired of this request, but per your comment here I think I might have a lightweight-enough solution for you to consider.

This solution keeps browser-native scrolling, doesn’t require any ScrollSync-esque behavior, and only requires a few changes to the createGridComponent render method. However, IE11 is not supported. I think this is OK as long as it’s explicitly called out in the documentation.

You can see a demo here: https://codesandbox.io/s/xjzrzmxv8q

This is what I have locally as a prototype - if you’re interested in a PR for this, there’s definitely some more work to be done as several things are hardcoded. I can do the work here, but would appreciate some guidance.

I totally understand if you don’t want to add this - in the worst case, this issue can at least document the approach for others.

An overview of my changes:

First, when generating items, we want to skip row == 0 and column == 0 (the Math.max() calls). We also push into leftStickyItems and topStickyItems for the visible range. Those are the only changes here.

const items = [];
const topStickyItems = [];
const leftStickyItems = [];

if (columnCount > 0 && rowCount) {
  for (
    let columnIndex = Math.max(1, columnStartIndex); // Skip column 0
    columnIndex <= columnStopIndex;
    columnIndex++
  ) {
    topStickyItems.push(
      createElement(children, {
        columnIndex,
        data: itemData,
        isScrolling: useIsScrolling ? isScrolling : undefined,
        key: itemKey({ columnIndex, data: itemData, rowIndex: 0 }),
        rowIndex: 0,
        style: this._getItemStyle(0, columnIndex),
      })
    );
  }

  for (
    let rowIndex = Math.max(1, rowStartIndex); // Skip row 0
    rowIndex <= rowStopIndex;
    rowIndex++
  ) {
    // I'm leveraging this already existing loop, but this is probably
    // better as its own loop for clarity.
    leftStickyItems.push(
      createElement(children, {
        columnIndex: 0,
        data: itemData,
        isScrolling: useIsScrolling ? isScrolling : undefined,
        key: itemKey({ columnIndex: 0, data: itemData, rowIndex }),
        rowIndex,
        style: this._getItemStyle(rowIndex, 0),
      })
    );

    for (
      let columnIndex = Math.max(1, columnStartIndex); // Skip column 0
      columnIndex <= columnStopIndex;
      columnIndex++
    ) {
      items.push(
        createElement(children, {
          columnIndex,
          data: itemData,
          isScrolling: useIsScrolling ? isScrolling : undefined,
          key: itemKey({ columnIndex, data: itemData, rowIndex }),
          rowIndex,
          style: this._getItemStyle(rowIndex, columnIndex),
        })
      );
    }
  }
}

Once we’ve generated those items, before the return call, we add the sticky containers to the items array.

const topLeftStyle = this._getItemStyle(0, 0);

items.unshift(
  createElement('div', {
    children: leftStickyItems,
    key: 'left-sticky',
    className: 'left-sticky',
    style: {
      height: estimatedTotalHeight,
      width: topLeftStyle.width,
      position: 'sticky',
      left: 0,
      zIndex: 1,
      transform: `translateY(-${topLeftStyle.height}px)`,
    },
  })
);

items.unshift(
  createElement('div', {
    children: topStickyItems,
    key: 'top-sticky',
    className: 'top-sticky',
    style: {
      height: topLeftStyle.height,
      width: estimatedTotalWidth,
      position: 'sticky',
      top: 0,
      zIndex: 1,
    },
  })
);

There’s one unsolved part - the (0, 0) position. I’ve gotten around this by rendering it outside of the grid (the grey box in the top left). This is fine for my use case but would merit a better solution if you want a PR for this.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:6
  • Comments:14 (4 by maintainers)

github_iconTop GitHub Comments

16reactions
astorchouscommented, Mar 4, 2020

@bvaughn First of all man I would like to thank you for this awesome lib. Provided abstraction level allows to resolve almost whatever you want. Now back to the topic. My production case is pretty complicated: multiple grouping against rows and columns which should be sticky. So I tried to simplify it as it was possible and made this sandbox based on sticky list implementation: https://codesandbox.io/s/react-window-sticky-grid-liwsd.

4reactions
MarkLeMerisecommented, Mar 22, 2019

@apazzolini I just wanted to weigh in here and say that I’ve been playing with your branch all day and it works really well! My particular use case has to render a 5000-cell (100 rows x 50 columns) table and my first attempt with scroll-syncing was okay, but noticeably laggy. This native CSS solution is really smooth.

I understand this probably wouldn’t (and shouldn’t) be a core library feature, but hopefully it’ll find more exposure for other devs who need stick/frozen columns and rows.

Thanks for the great work, @apazzolini!

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to Use CSS Grid for Sticky Headers and Footers
How to Use CSS Grid for Sticky Headers and Footers ... Unsurprisingly, it uses fixed positioning, which means it has a hard-coded size....
Read more >
Fixed header in CSS Grid - Stack Overflow
In 2018, you can use position: sticky header { position: sticky; top: 0; }. Here is a JSFiddle demoing it.
Read more >
Using Position Sticky With CSS Grid - Ahmad Shadeed
Simple, right? Now, we want to add position: sticky to the aside element to make it fixed while scrolling.
Read more >
How I Use CSS Grid for Sticky Headers - DEV Community ‍ ‍
Sticky headers traditionally get positioned with display: absolute; or display: fixed; . While this does work, there is a much more elegant ...
Read more >
CSS Grid with fixed headers and columns - CodePen
Insecure Resource. The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found