Deep Research: a tab switch that takes the scrolled content away brings the header back

The header hides while the gallery's content is scrolled down and returns at its top. Switching
tabs hides the old tab's pane (or removes its map) without any scroll event, so after scrolling a
tab down and switching to a map-only tab (Prayer Progress, Cities with Internet, Gospel Resources
on a phone) the header stayed hidden, and nothing on that tab could bring it back. The page now
remembers which scroller hid the header; when a change in the gallery hides or removes it, the
header returns. The new tab starts with the header shown, and if it scrolls, the next scroll hides
it again.
This commit is contained in:
driver 2026-09-30 04:06:23 +00:00
parent 36efb6d861
commit fac7bd8436

View file

@ -57,11 +57,19 @@ function fitBelowHeader() {
function watchPaneForHeader(el: HTMLElement, root: ShadowRoot) { function watchPaneForHeader(el: HTMLElement, root: ShadowRoot) {
const cls = document.documentElement.classList const cls = document.documentElement.classList
const large = (n: HTMLElement) => n.clientHeight >= el.clientHeight / 2 const large = (n: HTMLElement) => n.clientHeight >= el.clientHeight / 2
// The scroller whose scroll hid the header.
let hider: HTMLElement | null = null
const onScroll = (e: Event) => { const onScroll = (e: Event) => {
const pane = e.target const pane = e.target
if (!(pane instanceof HTMLElement) || !large(pane)) return if (!(pane instanceof HTMLElement) || !large(pane)) return
if (pane.scrollTop > 8) cls.add('rg-header-hidden') if (pane.scrollTop > 8) {
else if (pane.scrollTop <= 0) cls.remove('rg-header-hidden') hider = pane
cls.add('rg-header-hidden')
}
else if (pane.scrollTop <= 0) {
hider = null
cls.remove('rg-header-hidden')
}
} }
// Scroll events don't bubble, but they do pass the gallery's shadow root in the capture phase. // Scroll events don't bubble, but they do pass the gallery's shadow root in the capture phase.
root.addEventListener('scroll', onScroll, { capture: true, passive: true }) root.addEventListener('scroll', onScroll, { capture: true, passive: true })
@ -84,10 +92,29 @@ function watchPaneForHeader(el: HTMLElement, root: ShadowRoot) {
} }
el.addEventListener('touchmove', discover, { passive: true }) el.addEventListener('touchmove', discover, { passive: true })
el.addEventListener('wheel', discover, { passive: true }) el.addEventListener('wheel', discover, { passive: true })
// Switching tabs hides the old tab's pane (or removes its map) without any scroll event. If
// that takes away the scroller that hid the header, the header comes back: a map-only tab has
// nothing to scroll back to the top, so it could never bring the header back itself.
let pending = false
const recheck = () => {
pending = false
if (!cls.contains('rg-header-hidden')) return
if (hider && hider.isConnected && hider.getClientRects().length > 0 && hider.scrollTop > 0) return
hider = null
cls.remove('rg-header-hidden')
}
const tabSwitch = new MutationObserver(() => {
if (!pending) {
pending = true
requestAnimationFrame(recheck)
}
})
tabSwitch.observe(root, { subtree: true, childList: true, attributes: true, attributeFilter: ['hidden'] })
unwatchPane = () => { unwatchPane = () => {
root.removeEventListener('scroll', onScroll, true) root.removeEventListener('scroll', onScroll, true)
el.removeEventListener('touchmove', discover) el.removeEventListener('touchmove', discover)
el.removeEventListener('wheel', discover) el.removeEventListener('wheel', discover)
tabSwitch.disconnect()
} }
} }