( fragment: string | undefined, shouldContinue: () => boolean = () => true )
| 156 | * navigation supersedes this one. It is checked between async steps. |
| 157 | */ |
| 158 | export const scrollToFragment = async ( |
| 159 | fragment: string | undefined, |
| 160 | shouldContinue: () => boolean = () => true |
| 161 | ): Promise<boolean> => { |
| 162 | if (fragment == null || fragment === '') { |
| 163 | return false; |
| 164 | } |
| 165 | |
| 166 | // URL fragments are percent-encoded but element ids are not; decode for |
| 167 | // matching per the HTML spec's indicated-element resolution. |
| 168 | let decoded: string; |
| 169 | try { |
| 170 | decoded = decodeURIComponent(fragment); |
| 171 | } catch { |
| 172 | decoded = fragment; |
| 173 | } |
| 174 | |
| 175 | const target = await findFragmentTarget(decoded, shouldContinue); |
| 176 | if (!target || !shouldContinue()) { |
| 177 | return false; |
| 178 | } |
| 179 | |
| 180 | // Best-effort scroll: swallow exceptions if the page tears down mid-animation. |
| 181 | try { |
| 182 | const contentHost = findClosestIonContent(target); |
| 183 | if (contentHost && isIonContent(contentHost)) { |
| 184 | const content = contentHost as HTMLIonContentElement; |
| 185 | const scrollEl = await getScrollElement(content); |
| 186 | // Yield one frame so the newly mounted target's layout is stable |
| 187 | // before we measure its rect. |
| 188 | await nextFrame(); |
| 189 | if (!shouldContinue()) return false; |
| 190 | const targetRect = target.getBoundingClientRect(); |
| 191 | const scrollRect = scrollEl.getBoundingClientRect(); |
| 192 | const top = targetRect.top - scrollRect.top + scrollEl.scrollTop; |
| 193 | // Preserve scrollLeft so RTL and horizontally-scrolling pages aren't reset. |
| 194 | await content.scrollToPoint(scrollEl.scrollLeft, top, FRAGMENT_SCROLL_DURATION); |
| 195 | } else { |
| 196 | target.scrollIntoView({ behavior: 'smooth' }); |
| 197 | } |
| 198 | return true; |
| 199 | } catch (e) { |
| 200 | printIonError('[ion-router] - Exception in scrollToFragment:', e); |
| 201 | return false; |
| 202 | } |
| 203 | }; |
| 204 | |
| 205 | export const waitUntilNavNode = (): Promise<void> => { |
| 206 | if (searchNavNode(document.body)) { |
no test coverage detected