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) {
// An article that has scrolled past the (possibly sticky-bar-shrunk) top
// edge of the viewport (not intersecting, bounding box above that edge)
// 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.
function handleIntersection(entries, topbarHeight = 0) {
// Resolve all affected feeds before touching feeds.value — the target.id
// indices are render-time positions that shift once we splice the array.
const readFeeds = entries
.filter(entry => initialLoad === true && !entry.isIntersecting && entry.boundingClientRect.y < topbarHeight)
.map(entry => feeds.value[entry.target.id])
@@ -214,22 +210,38 @@ async function handleIntersection(entries, topbarHeight = 0) {
if (readFeeds.length === 0) return
for (const feed of readFeeds) {
await markRead(feed.id)
// Disconnect before the DOM mutation. In card layout the cards are short
// 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))
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
// scroll anchoring (on by default, no overflow-anchor override here)
// 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()`
// to fix a past scroll-jump complaint, but by the time several articles
// have been read, id "0" is an already-read element far above the
// viewport — forcing a jump to it fights scroll anchoring and is exactly
// the stutter being fixed now.
for (const feed of readFeeds) {
markRead(feed.id)
}
nextTick().then(() => {
// If scroll anchoring didn't compensate for the removed content (common
// with position:fixed headers and overflow-x:hidden on body), the first
// remaining article will have drifted above the header. Correct the scroll
// 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) {
@@ -295,6 +307,7 @@ async function toggleLayout() {
observer.disconnect()
observer = null
}
window.scrollTo(0, 0)
layout.value = layout.value === 'list' ? 'cards' : 'list'
localStorage.setItem('layout', layout.value)
await nextTick()