fix autoscroll

This commit is contained in:
2026-06-16 16:26:33 +02:00
parent 967803c326
commit 4d3f5d3285
+32 -19
View File
@@ -200,13 +200,9 @@ function setupIntersectionObserver() {
} }
} }
async function handleIntersection(entries, topbarHeight = 0) { function handleIntersection(entries, topbarHeight = 0) {
// An article that has scrolled past the (possibly sticky-bar-shrunk) top // Resolve all affected feeds before touching feeds.value — the target.id
// edge of the viewport (not intersecting, bounding box above that edge) // indices are render-time positions that shift once we splice the array.
// has been read. Resolve all affected feeds up front, before any removal —
// splicing `feeds` while iterating would shift the array indices that later
// entries' `target.id` refer to, causing the wrong item to be marked read
// and removed.
const readFeeds = entries const readFeeds = entries
.filter(entry => initialLoad === true && !entry.isIntersecting && entry.boundingClientRect.y < topbarHeight) .filter(entry => initialLoad === true && !entry.isIntersecting && entry.boundingClientRect.y < topbarHeight)
.map(entry => feeds.value[entry.target.id]) .map(entry => feeds.value[entry.target.id])
@@ -214,22 +210,38 @@ async function handleIntersection(entries, topbarHeight = 0) {
if (readFeeds.length === 0) return if (readFeeds.length === 0) return
for (const feed of readFeeds) { // Disconnect before the DOM mutation. In card layout the cards are short
await markRead(feed.id) // enough that the shift caused by removing one can push the next card above
// the header, which the observer would immediately treat as another read —
// cascading until many articles disappear at once.
if (observer) {
observer.disconnect()
observer = null
} }
const readIds = new Set(readFeeds.map(feed => feed.id)) const readIds = new Set(readFeeds.map(feed => feed.id))
feeds.value = feeds.value.filter(feed => !readIds.has(feed.id)) feeds.value = feeds.value.filter(feed => !readIds.has(feed.id))
// Removing .observe nodes that have already scrolled above the viewport
// shrinks the document above the current scroll position — native CSS for (const feed of readFeeds) {
// scroll anchoring (on by default, no overflow-anchor override here) markRead(feed.id)
// compensates for that automatically by keeping the visually-anchored }
// node in place. Do NOT add a scrollIntoView()/scrollTo() here: an
// earlier version called `document.getElementById(0)?.scrollIntoView()` nextTick().then(() => {
// to fix a past scroll-jump complaint, but by the time several articles // If scroll anchoring didn't compensate for the removed content (common
// have been read, id "0" is an already-read element far above the // with position:fixed headers and overflow-x:hidden on body), the first
// viewport — forcing a jump to it fights scroll anchoring and is exactly // remaining article will have drifted above the header. Correct the scroll
// the stutter being fixed now. // position so it sits exactly at the header bottom before reconnecting —
// otherwise the initial observation would immediately mark everything above
// the topbar as read and cascade until the list is empty.
const first = document.querySelector('.observe')
if (first) {
const top = first.getBoundingClientRect().top
if (top < topbarHeight) {
window.scrollBy(0, top - topbarHeight)
}
}
setupIntersectionObserver()
})
} }
function setInitialLoad(value) { function setInitialLoad(value) {
@@ -295,6 +307,7 @@ async function toggleLayout() {
observer.disconnect() observer.disconnect()
observer = null observer = null
} }
window.scrollTo(0, 0)
layout.value = layout.value === 'list' ? 'cards' : 'list' layout.value = layout.value === 'list' ? 'cards' : 'list'
localStorage.setItem('layout', layout.value) localStorage.setItem('layout', layout.value)
await nextTick() await nextTick()