ngx-virtual-scroller
ngx-virtual-scroller copied to clipboard
Fixed table header broken when cells have "position: relative" content
When cell content uses position: relative, its painted on top of the fixed header.
For example, when the sample table looks like this:
<virtual-scroller #scroll
[items]="filteredList">
<table>
<thead #header>
<th>Index</th>
<th>Name</th>
<th>Gender</th>
<th>Age</th>
<th>Address</th>
</thead>
<tbody #container>
<tr *ngFor="let item of scroll.viewPortItems">
<td><div style="position: relative; color: red; font-weight: bold">{{item.index}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.name}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.gender}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.age}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.address}}</div></td>
</tr>
</tbody>
</table>
</virtual-scroller>
... then it's rendered like:

Interesting.
I'm not sure the cause or the correct fix here.
My first guess was the useMarginInsteadOfTranslate flag might help (because CSS transform changes the behavior of position: relative). However, there's 2 issues with that. 1) the
isn't underneath the , so I'm not sure that's the root cause of the bug (although table's have weird rules). 2) When we originally coded fixed headers we tried margin instead of translate & it had no effect on so the only way we could find to get a working fixed header was with translate.I almost found a hacky workaround, but it's not quite working. If you add "z-index: -1;" to the div which has position: relative, it fixes it. The text goes behind the header. However, that only works if you do it from the chrome inspector. If you do it in code, nothing renders at all. I haven't tested in other browsers. My guess is there's some layout stuff that gets skipped, so we'd have to use javascript to add the z-index after the items are first rendered.
Maybe do something like this?
table.alreadyRendered tbody td div {
z-index: 1;
}
and trigger [class.alreadyRendered]="true" after some event/timeout?
Another option that comes to mind is modifying the math inside ngx-virtual-scroller to render 1 fewer item if the fixed header is enabled. I think when we originally coded it we didn't bother with that because the header seemed to render on top of the 1st row so it didn't matter.
Thoughts?
Hope you can come up with a decent workaround for your app. If you find something that works well & you can make it generic enough for everyone else, please submit a Pull Request.
Thanks.
Fixed header now uses translate, but I think that it's better to use position: sticky. In the similar case it worked fine
@kykint Do you want to code this & submit a PR? We should also add a sample with table & position: relative to the demo as a test-case so we can prove it's fixed.
For now, in my use case I decided to use two tables: just header, then another (scrollable) with just body. It works for me, but in the general case it's getting tricky wrt column width synchronization.
I'm still trying to validate this in other browsers, but it seems like swapping the thead and the tbody in the HTML fixes this issue.
<virtual-scroller #scroll
[items]="filteredList">
<table>
<tbody #container>
<tr *ngFor="let item of scroll.viewPortItems">
<td><div style="position: relative; color: red; font-weight: bold">{{item.index}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.name}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.gender}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.age}}</div></td>
<td><div style="position: relative; color: red; font-weight: bold">{{item.address}}</div></td>
</tr>
</tbody>
<thead #header>
<th>Index</th>
<th>Name</th>
<th>Gender</th>
<th>Age</th>
<th>Address</th>
</thead>
</table>
</virtual-scroller>
@natelokers Interesting idea. I'm guessing that's similar to setting the z-index for thead higher than tbody. I've tested on chrome/firefox on windows & it fixes it for both of them. Edge/IE don't seem to work with ngx-virtual-scroller tables at all, regardless of thead/tbody order or position static/relative.
Maybe we should adjust the readme to suggest this approach.
I merged a PR which partially fixes the IE/Edge issue. https://github.com/rintoj/ngx-virtual-scroller/pull/282
@natelokers feel free to submit a PR with the demo & readme modified to suggest putting the thead after the tbody. Although we haven't tested every browser, I'm guessing this will fix it for most of them.
I'm still trying to validate this in other browsers, but it seems like swapping the thead and the tbody in the HTML fixes this issue.
<virtual-scroller #scroll [items]="filteredList"> <table> <tbody #container> <tr *ngFor="let item of scroll.viewPortItems"> <td><div style="position: relative; color: red; font-weight: bold">{{item.index}}</div></td> <td><div style="position: relative; color: red; font-weight: bold">{{item.name}}</div></td> <td><div style="position: relative; color: red; font-weight: bold">{{item.gender}}</div></td> <td><div style="position: relative; color: red; font-weight: bold">{{item.age}}</div></td> <td><div style="position: relative; color: red; font-weight: bold">{{item.address}}</div></td> </tr> </tbody> <thead #header> <th>Index</th> <th>Name</th> <th>Gender</th> <th>Age</th> <th>Address</th> </thead> </table> </virtual-scroller>
this really helps. Thanks!
@konrad-garus @bbv-hungtran Do you still have the problem ? The issue is 2 years old
The problem is still in version 4.0.3.
But does work with the suggestion by @natelokers.