diff --git a/src/cdk/scrolling/scrolling.md b/src/cdk/scrolling/scrolling.md
index aec8935d065a..656cacce954b 100644
--- a/src/cdk/scrolling/scrolling.md
+++ b/src/cdk/scrolling/scrolling.md
@@ -128,3 +128,21 @@ to the list as the user scrolls without removing rendered views. The `appendOnly
views that are already rendered persist in the DOM after they scroll out of view.
+
+### Separate viewport and scrolling element
+The virtual scroll viewport itself acts as the scrolling element by default. However, there may be
+some cases where you want to have the viewport scroll one of its parent elements. For example,
+if you want to have some non-virtualized content that the user can scroll through before or after
+the virtualized content.
+
+To configure a `cdk-vritual-scroll-viewport` to use one of its parent elements as the scrolling
+element, apply `cdkVirtualScrollingElement` to the scrolling parent element.
+
+
+
+Another common scenario is using the window itself as the scrolling element. This often a better
+user experience on mobile devices, as it allows the browser chrome to scroll away. To use the
+window as the scrolling element, add the `scrollWindow` attribute to the
+`cdk-virtual-scroll-viewport`.
+
+
diff --git a/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.css b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.css
new file mode 100644
index 000000000000..9514ed704e1d
--- /dev/null
+++ b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.css
@@ -0,0 +1,16 @@
+.example-viewport {
+ flex: 1;
+ width: 200px;
+ min-height: 200px;
+ border: 1px solid black;
+}
+
+.example-item {
+ height: 50px;
+}
+
+.example-header,
+.example-footer {
+ height: 100px;
+ background: lightgray;
+}
diff --git a/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.html b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.html
new file mode 100644
index 000000000000..e45c20ab44d6
--- /dev/null
+++ b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.html
@@ -0,0 +1,7 @@
+
diff --git a/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.ts b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.ts
new file mode 100644
index 000000000000..0072e7550735
--- /dev/null
+++ b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example.ts
@@ -0,0 +1,12 @@
+import {ChangeDetectionStrategy, Component} from '@angular/core';
+
+/** @title Virtual scrolling viewport parent element */
+@Component({
+ selector: 'cdk-virtual-scroll-parent-scrolling-example',
+ styleUrls: ['cdk-virtual-scroll-parent-scrolling-example.css'],
+ templateUrl: 'cdk-virtual-scroll-parent-scrolling-example.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CdkVirtualScrollParentScrollingExample {
+ items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
+}
diff --git a/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.css b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.css
new file mode 100644
index 000000000000..d883681ffb32
--- /dev/null
+++ b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.css
@@ -0,0 +1,9 @@
+.example-item {
+ height: 50px;
+}
+
+.example-header,
+.example-footer {
+ height: 100px;
+ background: lightgray;
+}
diff --git a/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.html b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.html
new file mode 100644
index 000000000000..0020c02846a5
--- /dev/null
+++ b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.html
@@ -0,0 +1,9 @@
+
+
+
+ {{item}}
+
+
+
+
+Please open on StackBlitz to see result
diff --git a/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.ts b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.ts
new file mode 100644
index 000000000000..ddebaa795817
--- /dev/null
+++ b/src/components-examples/cdk/scrolling/cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example.ts
@@ -0,0 +1,14 @@
+import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
+
+/** @title Virtual scrolling window */
+@Component({
+ selector: 'cdk-virtual-scroll-window-scrolling-example',
+ styleUrls: ['cdk-virtual-scroll-window-scrolling-example.css'],
+ templateUrl: 'cdk-virtual-scroll-window-scrolling-example.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CdkVirtualScrollWindowScrollingExample {
+ @Input() shouldRun = /(^|.)(stackblitz|webcontainer).(io|com)$/.test(window.location.host);
+
+ items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
+}
diff --git a/src/components-examples/cdk/scrolling/index.ts b/src/components-examples/cdk/scrolling/index.ts
index b15821361387..ac3fa81de59a 100644
--- a/src/components-examples/cdk/scrolling/index.ts
+++ b/src/components-examples/cdk/scrolling/index.ts
@@ -8,7 +8,10 @@ import {CdkVirtualScrollDlExample} from './cdk-virtual-scroll-dl/cdk-virtual-scr
import {CdkVirtualScrollFixedBufferExample} from './cdk-virtual-scroll-fixed-buffer/cdk-virtual-scroll-fixed-buffer-example';
import {CdkVirtualScrollHorizontalExample} from './cdk-virtual-scroll-horizontal/cdk-virtual-scroll-horizontal-example';
import {CdkVirtualScrollOverviewExample} from './cdk-virtual-scroll-overview/cdk-virtual-scroll-overview-example';
+import {CdkVirtualScrollParentScrollingExample} from './cdk-virtual-scroll-parent-scrolling/cdk-virtual-scroll-parent-scrolling-example';
import {CdkVirtualScrollTemplateCacheExample} from './cdk-virtual-scroll-template-cache/cdk-virtual-scroll-template-cache-example';
+import {CdkVirtualScrollWindowScrollingExample} from './cdk-virtual-scroll-window-scrolling/cdk-virtual-scroll-window-scrolling-example';
+import {CommonModule} from '@angular/common';
export {
CdkVirtualScrollAppendOnlyExample,
@@ -20,6 +23,8 @@ export {
CdkVirtualScrollHorizontalExample,
CdkVirtualScrollOverviewExample,
CdkVirtualScrollTemplateCacheExample,
+ CdkVirtualScrollParentScrollingExample,
+ CdkVirtualScrollWindowScrollingExample,
};
const EXAMPLES = [
@@ -32,10 +37,12 @@ const EXAMPLES = [
CdkVirtualScrollHorizontalExample,
CdkVirtualScrollOverviewExample,
CdkVirtualScrollTemplateCacheExample,
+ CdkVirtualScrollParentScrollingExample,
+ CdkVirtualScrollWindowScrollingExample,
];
@NgModule({
- imports: [ScrollingModule],
+ imports: [CommonModule, ScrollingModule],
declarations: EXAMPLES,
exports: EXAMPLES,
})
diff --git a/src/dev-app/virtual-scroll/BUILD.bazel b/src/dev-app/virtual-scroll/BUILD.bazel
index c99588166bb4..a421be855ea9 100644
--- a/src/dev-app/virtual-scroll/BUILD.bazel
+++ b/src/dev-app/virtual-scroll/BUILD.bazel
@@ -12,6 +12,7 @@ ng_module(
deps = [
"//src/cdk-experimental/scrolling",
"//src/cdk/scrolling",
+ "//src/components-examples/cdk/scrolling",
"//src/material/button",
"//src/material/form-field",
"//src/material/input",
diff --git a/src/dev-app/virtual-scroll/virtual-scroll-demo.html b/src/dev-app/virtual-scroll/virtual-scroll-demo.html
index c70ddd7c9ed1..76c17f993488 100644
--- a/src/dev-app/virtual-scroll/virtual-scroll-demo.html
+++ b/src/dev-app/virtual-scroll/virtual-scroll-demo.html
@@ -179,24 +179,10 @@ Append only
-Custom virtual scroller
-
-
-
Content before virtual scrolling items
-
-
- Item #{{i}} - ({{size}}px)
-
-
-
Content after virtual scrolling items
-
-
-Window virtual scroller
-
-
-
- Item #{{i}} - ({{size}}px)
-
-
+Virtual scroller with scrolling parent
+
+
+
+Virtual scroller with scrolling window
+
+
diff --git a/src/dev-app/virtual-scroll/virtual-scroll-demo.scss b/src/dev-app/virtual-scroll/virtual-scroll-demo.scss
index 3fcffe2c52c2..3371b63e67c6 100644
--- a/src/dev-app/virtual-scroll/virtual-scroll-demo.scss
+++ b/src/dev-app/virtual-scroll/virtual-scroll-demo.scss
@@ -53,3 +53,14 @@
.demo-td {
border: 1px solid gray;
}
+
+.demo-resize-example {
+ display: flex;
+ width: 500px;
+ height: 500px;
+}
+
+cdk-virtual-scroll-window-scrolling-example {
+ display: block;
+ width: 500px;
+}
diff --git a/src/dev-app/virtual-scroll/virtual-scroll-demo.ts b/src/dev-app/virtual-scroll/virtual-scroll-demo.ts
index 5966f9ed270a..bea4e5775491 100644
--- a/src/dev-app/virtual-scroll/virtual-scroll-demo.ts
+++ b/src/dev-app/virtual-scroll/virtual-scroll-demo.ts
@@ -16,6 +16,7 @@ import {MatFormFieldModule} from '@angular/material/form-field';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {BehaviorSubject} from 'rxjs';
+import {CdkScrollingExamplesModule} from '@angular/components-examples/cdk/scrolling';
type State = {
name: string;
@@ -38,6 +39,7 @@ type State = {
MatInputModule,
MatSelectModule,
ScrollingModule,
+ CdkScrollingExamplesModule,
],
})
export class VirtualScrollDemo implements OnDestroy {