(fragment: string, shouldContinue: () => boolean)
| 115 | * last-to-first order preserves leaf-most preference for nested outlets. |
| 116 | */ |
| 117 | const findFragmentTarget = async (fragment: string, shouldContinue: () => boolean): Promise<HTMLElement | null> => { |
| 118 | // CSS.escape is unavailable on very old WebViews; the fallback path uses |
| 119 | // `getElementById` and drops the legacy `<a name>` branch. |
| 120 | const canEscape = typeof CSS !== 'undefined' && typeof CSS.escape === 'function'; |
| 121 | const escaped = canEscape ? CSS.escape(fragment) : null; |
| 122 | |
| 123 | for (let i = 0; i < FRAGMENT_POLL_FRAMES; i++) { |
| 124 | if (!shouldContinue()) return null; |
| 125 | |
| 126 | let candidates: HTMLElement[] = []; |
| 127 | if (escaped !== null) { |
| 128 | try { |
| 129 | candidates = [...document.querySelectorAll<HTMLElement>(`#${escaped}, a[name="${escaped}"]`)]; |
| 130 | } catch { |
| 131 | candidates = [...document.querySelectorAll<HTMLElement>(`#${escaped}`)]; |
| 132 | } |
| 133 | } else { |
| 134 | const byId = document.getElementById(fragment); |
| 135 | if (byId !== null) candidates = [byId]; |
| 136 | } |
| 137 | |
| 138 | for (let j = candidates.length - 1; j >= 0; j--) { |
| 139 | if (isInActivePage(candidates[j])) { |
| 140 | return candidates[j]; |
| 141 | } |
| 142 | } |
| 143 | await nextFrame(); |
| 144 | } |
| 145 | |
| 146 | return null; |
| 147 | }; |
| 148 | |
| 149 | /** |
| 150 | * Scrolls to the element whose id matches `fragment`, falling back to a legacy |
no test coverage detected