diff --git a/src/cdk-experimental/scrolling/virtual-for-of.ts b/src/cdk-experimental/scrolling/virtual-for-of.ts index 22c5b4ee5a0d..f5fa0dca3fe3 100644 --- a/src/cdk-experimental/scrolling/virtual-for-of.ts +++ b/src/cdk-experimental/scrolling/virtual-for-of.ts @@ -49,8 +49,13 @@ export type CdkVirtualForOfContext = { }; -/** Helper to extract size from a ClientRect. */ -function getSize(orientation: 'horizontal' | 'vertical', rect: ClientRect): number { +/** Helper to extract size from a DOM Node. */ +function getSize(orientation: 'horizontal' | 'vertical', node: Node): number { + const el = node as Element; + if (!el.getBoundingClientRect) { + return 0; + } + const rect = el.getBoundingClientRect(); return orientation == 'horizontal' ? rect.width : rect.height; } @@ -193,7 +198,6 @@ export class CdkVirtualForOf implements CollectionViewer, DoCheck, OnDestroy const rangeLen = range.end - range.start; // Loop over all root nodes for all items in the range and sum up their size. - // TODO(mmalerba): Make this work with non-element nodes. let totalSize = 0; let i = rangeLen; while (i--) { @@ -201,7 +205,7 @@ export class CdkVirtualForOf implements CollectionViewer, DoCheck, OnDestroy EmbeddedViewRef> | null; let j = view ? view.rootNodes.length : 0; while (j--) { - totalSize += getSize(orientation, (view!.rootNodes[j] as Element).getBoundingClientRect()); + totalSize += getSize(orientation, view!.rootNodes[j]); } } diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss index b68799167b1e..9b7c5f0377ed 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.scss @@ -1,3 +1,32 @@ +// When elements such as `` or `
  • ` are repeated inside the cdk-virtual-scroll-viewport, +// their container element (e.g. ``, `
      `, etc.) needs to be placed in the viewport as +// well. We reset some properties here to prevent these container elements from introducing +// additional space that would throw off the the scrolling calculations. +@mixin _cdk-virtual-scroll-clear-container-space($direction) { + $start: if($direction == horizontal, 'left', 'top'); + $end: if($direction == horizontal, 'right', 'bottom'); + + & > dl:not([cdkVirtualFor]), + & > ol:not([cdkVirtualFor]), + & > table:not([cdkVirtualFor]), + & > ul:not([cdkVirtualFor]) { + padding: { + #{$start}: 0; + #{$end}: 0; + } + margin: { + #{$start}: 0; + #{$end}: 0; + } + border: { + #{$start}-width: 0; + #{$end}-width: 0; + } + outline: none; + } +} + + // Scrolling container. cdk-virtual-scroll-viewport { display: block; @@ -16,10 +45,12 @@ cdk-virtual-scroll-viewport { .cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper { bottom: 0; + @include _cdk-virtual-scroll-clear-container-space(horizontal); } .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper { right: 0; + @include _cdk-virtual-scroll-clear-container-space(vertical); } // Spacer element that whose width or height will be adjusted to match the size of the entire data diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts b/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts index 2aea398549de..6fe679a20a54 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.spec.ts @@ -27,7 +27,7 @@ describe('CdkVirtualScrollViewport', () => { finishInit(fixture); const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper'); + viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; expect(contentWrapper.children.length) .toBe(4, 'should render 4 50px items to fill 200px space'); })); @@ -507,7 +507,7 @@ describe('CdkVirtualScrollViewport', () => { finishInit(fixture); const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper'); + viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; expect(contentWrapper.children.length) .toBe(4, 'should render 4 50px items to fill 200px space'); })); @@ -517,7 +517,7 @@ describe('CdkVirtualScrollViewport', () => { finishInit(fixture); const contentWrapper = - viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper'); + viewport.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-content-wrapper')!; expect(contentWrapper.children.length).toBe(4, 'should render 4 items to fill 200px space based on 50px estimate from first item'); })); diff --git a/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts b/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts index d6e456a262b6..670946ca305c 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts +++ b/src/cdk-experimental/scrolling/virtual-scroll-viewport.ts @@ -57,7 +57,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { @Input() orientation: 'horizontal' | 'vertical' = 'vertical'; /** The element that wraps the rendered content. */ - @ViewChild('contentWrapper') _contentWrapper: ElementRef; + @ViewChild('contentWrapper') _contentWrapper: ElementRef; /** A stream that emits whenever the rendered range changes. */ renderedRangeStream: Observable = this._renderedRangeSubject.asObservable(); @@ -67,7 +67,10 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { */ _totalContentSize = 0; - /** The the rendered content transform. */ + /** + * The CSS transform applied to the rendered subset of items so that they appear within the bounds + * of the visible viewport. + */ private _renderedContentTransform: string; /** The currently rendered range of indices. */ @@ -103,7 +106,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { /** A list of functions to run after the next change detection cycle. */ private _runAfterChangeDetection: Function[] = []; - constructor(public elementRef: ElementRef, + constructor(public elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef, private _ngZone: NgZone, @Inject(VIRTUAL_SCROLL_STRATEGY) private _scrollStrategy: VirtualScrollStrategy) {} @@ -327,7 +330,7 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy { } } - for (let fn of this._runAfterChangeDetection) { + for (const fn of this._runAfterChangeDetection) { fn(); } this._runAfterChangeDetection = []; diff --git a/src/cdk-experimental/scrolling/virtual-scroll.md b/src/cdk-experimental/scrolling/virtual-scroll.md index 12f275d88f65..9f892254573e 100644 --- a/src/cdk-experimental/scrolling/virtual-scroll.md +++ b/src/cdk-experimental/scrolling/virtual-scroll.md @@ -142,3 +142,21 @@ content. ... ``` + +### Elements with parent tag requirements +Some HTML elements such as `
    ` and `
  • ` have limitations on the kinds of parent elements they +can be placed inside. To enable virtual scrolling over these type of elements, place the elements in +their proper parent, and then wrap the whole thing in a `cdk-virtual-scroll-viewport`. Be careful +that the parent does not introduce additional space (e.g. via `margin` or `padding`) as it will +interfere with the scrolling. + +```html + +
  • + + + + +
    {{row.first}}{{row.second}}
    + +``` diff --git a/src/demo-app/virtual-scroll/virtual-scroll-demo.html b/src/demo-app/virtual-scroll/virtual-scroll-demo.html index 7c8b754eee04..f479593e64bb 100644 --- a/src/demo-app/virtual-scroll/virtual-scroll-demo.html +++ b/src/demo-app/virtual-scroll/virtual-scroll-demo.html @@ -94,3 +94,45 @@

    trackBy state name

    {{state.capital}}
    + +

    Use with <ol>

    + + +
      +
    1. + {{state.name}} - {{state.capital}} +
    2. +
    +
    + +

    Use with <ul>

    + + +
      +
    • + {{state.name}} - {{state.capital}} +
    • +
    +
    + +

    Use with <dl>

    + + +
    + +
    {{state.name}}
    +
    {{state.capital}}
    +
    +
    +
    + +

    Use with <table>

    + + + + + + + +
    {{state.name}}{{state.capital}}
    +
    diff --git a/src/demo-app/virtual-scroll/virtual-scroll-demo.scss b/src/demo-app/virtual-scroll/virtual-scroll-demo.scss index 58e9e1741b3c..0aa6a2a48e6f 100644 --- a/src/demo-app/virtual-scroll/virtual-scroll-demo.scss +++ b/src/demo-app/virtual-scroll/virtual-scroll-demo.scss @@ -37,3 +37,21 @@ .demo-capital { font-size: 14px; } + +.demo-dt { + height: 30px; + font-weight: bold; +} + +.demo-dd { + height: 30px; +} + +.demo-li, +.demo-td { + height: 50px; +} + +.demo-td { + border: 1px solid gray; +}