-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug(table): Sticky header flickering #21576
Comments
I wasn't able to reproduce it, but my guess is that it's due to the |
@crisbeto yes, I tried translateZ(0) and some other hacks. Nothing helps. I think you have powerful PC. It has perceptible effect for huge tables with heavy rows. E.g. this example has flickering every time I scroll by vertical scroll bar because it causes lots of renderings. For heavy table I see flickering during wheel scroll also. P.S. Other laptop with Windows 10 |
I see. It seems like we shouldn't really have to set the |
@crisbeto I think I can provide some context. I've spent a lot of time digging into implementing virtual scrolling with CDK tables recently and I've been able to address the flickering in my projects at work. Note: I'm using the latest Chrome on Windows 10 for testing this. The reason that https://stackblitz.com/edit/mat-table-virtual-scroll-div-qxryk4 The reason that these elements are translated in spite of their position property is that layout (including sticky positioning) is applied by the browser before translation is considered, according to the spec. Since the sticky row is inside both the viewport and the translated div, the browser positions it correctly and then translates it afterwards. Then when we set Using If you un-comment the cdk-virtual-scroll-viewport and comment out the app-table-virtual-scroll-viewport you can see the flickering return. Another solution is to move the sticky element(s) out of the transformed container so that they aren't affected by the translation. This isn't always possible, especially with components that require specific markup like tables. #20414 hints at doing that, but it's a bit unclear to me how that would work with virtual scrolling, and it only works for flex tables. Sorry for the essay. I hope it helps! |
I also spent many hours try to put table with header under the virtual scroll and it seems have too many problems. |
@lujian98 Yes, I have the same conclusion. Unfortunately cdk-table doesn't provide this way. Probably it is because of bad semantics. I think this can be sacrificed for the infinite scrolling that has been working on computers since the last century. |
@tamtakoe Below is what I did for the table header columns, and table data rows with virtual scroll.
|
Moving table headers and footers outside the virtual viewport will resolve the flickering. As @garrettld noted, I experimented with the possibility of this in #20414. There are some challenges with this solution that we're working to resolve:
|
@lujian98 I tried the same way, but I used native one-row table for header and custom directive which reconciles column width for header and body. It works perfect but it has really terrible UX for developers. Especially if you have many tables which should be supported by several developers. That is why I offered this way which allows to wrap e.g. cdk-table template to custom one. Unfortunately it was declined by angular team. |
Any fix for this issue? |
Inspired by @garrettld's findings above, I've written a directive that simply monkey-patches the import { afterNextRender, Directive, inject } from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
/**
* Monkey-patches the `CdkVirtualScrollViewport` component and modifies it to
* set "`top: {offset}px`" instead of "`transform: translateY({offset}px)`".
* This fixes a bug where elements (such as a table header row) with
* `position: sticky` don't correctly stick to the viewport, and enables you to
* remove any custom handling you may be doing to hook events and set the top of
* the stick elements manually.
*
* This currently works with Angular Material CDK 18.0.x, but uses
* private accessors and may break without warning. If so, re-replicate the
* `_doChangeDetection()` method in the `patchedDoChangeDetection()` function
* below.
*/
@Directive({
selector: 'cdk-virtual-scroll-viewport[stickyPatch]'
})
export class CdkVirtualScrollStickyPatchDirective {
private _viewport = inject(CdkVirtualScrollViewport);
constructor() {
this._viewport['_doChangeDetection'] = patchedDoChangeDetection;
}
}
const TRANSLATE_REGEX = /translate([XY])\((\d+)px\)/;
/**
* This is current as of Angular Material CDK version 18.0.x, but may change
* without warning. To update, replicate the `_doChangeDetection()` function
* from https://github.com/angular/components/blob/main/src/cdk/scrolling/virtual-scroll-viewport.ts
*
* @param this The CdkVirtualScrollViewport component.
*/
function patchedDoChangeDetection(this: CdkVirtualScrollViewport): void {
if (this['_isDestroyed']) {
return;
}
this['ngZone'].run(() => {
this['_changeDetectorRef'].markForCheck();
const match = this['_renderedContentTransform']?.match(TRANSLATE_REGEX);
if (match) {
const axis = match[1];
const offset = parseInt(match[2]);
if (axis === 'X') {
this._contentWrapper.nativeElement.style.left = `${offset}px`;
} else if (axis === 'Y') {
this._contentWrapper.nativeElement.style.top = `${offset}px`;
}
}
afterNextRender(
() => {
this['_isChangeDetectionPending'] = false;
const runAfterChangeDetection =
this['_runAfterChangeDetection'];
this['_runAfterChangeDetection'] = [];
for (const fn of runAfterChangeDetection) {
fn();
}
},
{ injector: this['_injector'] }
);
});
} And you use it like so: <cdk-virtual-scroll-viewport stickyPatch [itemSize]="35">
<table>
...
</table>
</cdk-virtual-scroll-viewport> |
@nickbelling Thank you for the fix. I tried it and I'm still seeing flickering if I scroll really fast which isn't necessarily a valid use case. I still like this solution more than event hooking on the sticky header. EDIT - I was using a mat-grid-list to create my table with the sticky header. After cutting over to mat-table with a sticky header the flickering is solved. Thanks again. |
Sticky header works with flickering with infinity scroll especially if scroll opposite direction. This is well-known issue for a couple of years, that appeared in some topics but still has no special topic.
Reproduction
Screen.Recording.2021-01-13.at.19.30.26.mov
E.g. with old version of Angular, but the same with last one. Also there are a lot of examples here #10122 (comment) or in other issues where the effect appeared
https://stackblitz.com/edit/mat-table-virtual-scroll-div
The text was updated successfully, but these errors were encountered: