From 36a096c9b60bd6b3b086f2c966a1cd40dbc54473 Mon Sep 17 00:00:00 2001 From: Amanda Smith <90629384+amandaesmith3@users.noreply.github.com> Date: Wed, 24 Nov 2021 08:40:36 -0600 Subject: [PATCH] fix(content): ensure scrollEl is always available in scroll methods (#24255) --- core/src/components/content/content.tsx | 29 +++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/core/src/components/content/content.tsx b/core/src/components/content/content.tsx index 290bd4c447..7696b0d692 100644 --- a/core/src/components/content/content.tsx +++ b/core/src/components/content/content.tsx @@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, List import { getIonMode } from '../../global/ionic-global'; import { Color, ScrollBaseDetail, ScrollDetail } from '../../interface'; +import { componentOnReady } from '../../utils/helpers'; import { isPlatform } from '../../utils/platform'; import { createColorClasses, hostContext } from '../../utils/theme'; @@ -25,7 +26,7 @@ export class Content implements ComponentInterface { private queued = false; private cTop = -1; private cBottom = -1; - private scrollEl!: HTMLElement; + private scrollEl?: HTMLElement; private isMainContent = true; // Detail is used in a hot loop in the scroll event, by allocating it here @@ -168,7 +169,7 @@ export class Content implements ComponentInterface { readTask(ts => { this.queued = false; this.detail.event = ev; - updateScrollDetail(this.detail, this.scrollEl, ts, shouldStart); + updateScrollDetail(this.detail, this.scrollEl!, ts, shouldStart); this.ionScroll.emit(this.detail); }); } @@ -183,8 +184,16 @@ export class Content implements ComponentInterface { * and `scrollToPoint()` to scroll the content into a certain point. */ @Method() - getScrollElement(): Promise { - return Promise.resolve(this.scrollEl); + async getScrollElement(): Promise { + /** + * If this gets called in certain early lifecycle hooks (ex: Vue onMounted), + * scrollEl won't be defined yet with the custom elements build, so wait for it to load in. + */ + if (!this.scrollEl) { + await new Promise(resolve => componentOnReady(this.el, resolve)); + } + + return Promise.resolve(this.scrollEl!); } /** @@ -203,8 +212,9 @@ export class Content implements ComponentInterface { * @param duration The amount of time to take scrolling to the bottom. Defaults to `0`. */ @Method() - scrollToBottom(duration = 0): Promise { - const y = this.scrollEl.scrollHeight - this.scrollEl.clientHeight; + async scrollToBottom(duration = 0): Promise { + const scrollEl = await this.getScrollElement(); + const y = scrollEl!.scrollHeight - scrollEl!.clientHeight; return this.scrollToPoint(undefined, y, duration); } @@ -216,8 +226,9 @@ export class Content implements ComponentInterface { * @param duration The amount of time to take scrolling by that amount. */ @Method() - scrollByPoint(x: number, y: number, duration: number): Promise { - return this.scrollToPoint(x + this.scrollEl.scrollLeft, y + this.scrollEl.scrollTop, duration); + async scrollByPoint(x: number, y: number, duration: number): Promise { + const scrollEl = await this.getScrollElement(); + return this.scrollToPoint(x + scrollEl!.scrollLeft, y + scrollEl!.scrollTop, duration); } /** @@ -229,7 +240,7 @@ export class Content implements ComponentInterface { */ @Method() async scrollToPoint(x: number | undefined | null, y: number | undefined | null, duration = 0): Promise { - const el = this.scrollEl; + const el = await this.getScrollElement(); if (duration < 32) { if (y != null) { el.scrollTop = y;