ngx-virtual-scroller icon indicating copy to clipboard operation
ngx-virtual-scroller copied to clipboard

Fixed table header broken when cells have "position: relative" content

Open konrad-garus opened this issue 7 years ago • 10 comments

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:

image

konrad-garus avatar Nov 28 '18 12:11 konrad-garus

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.

speige avatar Nov 28 '18 20:11 speige

Fixed header now uses translate, but I think that it's better to use position: sticky. In the similar case it worked fine

kykint avatar Nov 28 '18 20:11 kykint

@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.

speige avatar Nov 28 '18 21:11 speige

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.

konrad-garus avatar Nov 30 '18 12:11 konrad-garus

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 avatar Dec 10 '18 18:12 natelokers

@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.

speige avatar Dec 10 '18 18:12 speige

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.

speige avatar Dec 10 '18 18:12 speige

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!

bbv-hungtran avatar Apr 01 '19 06:04 bbv-hungtran

@konrad-garus @bbv-hungtran Do you still have the problem ? The issue is 2 years old

jfgreffier avatar Oct 21 '20 11:10 jfgreffier

The problem is still in version 4.0.3.

But does work with the suggestion by @natelokers.

markhbrown avatar Dec 14 '20 09:12 markhbrown