Skip to content

Commit 036729d

Browse files
crisbetojelbourn
authored andcommitted
fix(overlay): clear duplicate overlay container coming in from the server (#11940)
When coming from a server-side-rendered page, we may end up in a situation where there are multiple overlay containers on the page with stale overlays in them. These changes clear all old overlay containers before creating a new one. Relates to #11817.
1 parent 1c75040 commit 036729d

File tree

5 files changed

+44
-14
lines changed

5 files changed

+44
-14
lines changed

src/cdk/overlay/fullscreen-overlay-container.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ describe('FullscreenOverlayContainer', () => {
4747
createElement: function() {
4848
return document.createElement.apply(document, arguments);
4949
},
50+
getElementsByClassName: function() {
51+
return document.getElementsByClassName.apply(document, arguments);
52+
}
5053
};
5154

5255
return fakeDocument;

src/cdk/overlay/fullscreen-overlay-container.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,15 @@ export class FullscreenOverlayContainer extends OverlayContainer implements OnDe
6666

6767
private _getEventName(): string | undefined {
6868
if (!this._fullScreenEventName) {
69-
if (this._document.fullscreenEnabled) {
69+
const _document = this._document as any;
70+
71+
if (_document.fullscreenEnabled) {
7072
this._fullScreenEventName = 'fullscreenchange';
71-
} else if (this._document.webkitFullscreenEnabled) {
73+
} else if (_document.webkitFullscreenEnabled) {
7274
this._fullScreenEventName = 'webkitfullscreenchange';
73-
} else if ((this._document as any).mozFullScreenEnabled) {
75+
} else if (_document.mozFullScreenEnabled) {
7476
this._fullScreenEventName = 'mozfullscreenchange';
75-
} else if ((this._document as any).msFullscreenEnabled) {
77+
} else if (_document.msFullscreenEnabled) {
7678
this._fullScreenEventName = 'MSFullscreenChange';
7779
}
7880
}
@@ -85,10 +87,12 @@ export class FullscreenOverlayContainer extends OverlayContainer implements OnDe
8587
* Only that element and its children are visible when in fullscreen mode.
8688
*/
8789
getFullscreenElement(): Element {
88-
return this._document.fullscreenElement ||
89-
this._document.webkitFullscreenElement ||
90-
(this._document as any).mozFullScreenElement ||
91-
(this._document as any).msFullscreenElement ||
90+
const _document = this._document as any;
91+
92+
return _document.fullscreenElement ||
93+
_document.webkitFullscreenElement ||
94+
_document.mozFullScreenElement ||
95+
_document.msFullscreenElement ||
9296
null;
9397
}
9498
}

src/cdk/overlay/overlay-container.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ describe('OverlayContainer', () => {
5252
expect(containerElement.classList.contains('commander-shepard'))
5353
.toBe(false, 'Expected the overlay container not to have class "commander-shepard"');
5454
});
55+
56+
it('should ensure that there is only one overlay container on the page', () => {
57+
const extraContainer = document.createElement('div');
58+
extraContainer.classList.add('cdk-overlay-container');
59+
document.body.appendChild(extraContainer);
60+
61+
overlayContainer.getContainerElement();
62+
63+
expect(document.querySelectorAll('.cdk-overlay-container').length).toBe(1);
64+
});
5565
});
5666

5767
/** Test-bed component that contains a TempatePortal and an ElementRef. */

src/cdk/overlay/overlay-container.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import {
2121
@Injectable({providedIn: 'root'})
2222
export class OverlayContainer implements OnDestroy {
2323
protected _containerElement: HTMLElement;
24+
protected _document: Document;
2425

25-
constructor(@Inject(DOCUMENT) protected _document: any) {}
26+
constructor(@Inject(DOCUMENT) document: any) {
27+
this._document = document;
28+
}
2629

2730
ngOnDestroy() {
2831
if (this._containerElement && this._containerElement.parentNode) {
@@ -37,7 +40,10 @@ export class OverlayContainer implements OnDestroy {
3740
* @returns the container element
3841
*/
3942
getContainerElement(): HTMLElement {
40-
if (!this._containerElement) { this._createContainer(); }
43+
if (!this._containerElement) {
44+
this._createContainer();
45+
}
46+
4147
return this._containerElement;
4248
}
4349

@@ -46,9 +52,16 @@ export class OverlayContainer implements OnDestroy {
4652
* with the 'cdk-overlay-container' class on the document body.
4753
*/
4854
protected _createContainer(): void {
49-
const container = this._document.createElement('div');
55+
const containerClass = 'cdk-overlay-container';
56+
const previousContainers = this._document.getElementsByClassName(containerClass);
5057

51-
container.classList.add('cdk-overlay-container');
58+
// Remove any old containers. This can happen when transitioning from the server to the client.
59+
for (let i = 0; i < previousContainers.length; i++) {
60+
previousContainers[i].parentNode!.removeChild(previousContainers[i]);
61+
}
62+
63+
const container = this._document.createElement('div');
64+
container.classList.add(containerClass);
5265
this._document.body.appendChild(container);
5366
this._containerElement = container;
5467
}

tools/public_api_guard/cdk/overlay.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ export interface OverlayConnectionPosition {
196196

197197
export declare class OverlayContainer implements OnDestroy {
198198
protected _containerElement: HTMLElement;
199-
protected _document: any;
200-
constructor(_document: any);
199+
protected _document: Document;
200+
constructor(document: any);
201201
protected _createContainer(): void;
202202
getContainerElement(): HTMLElement;
203203
ngOnDestroy(): void;

0 commit comments

Comments
 (0)