stacker.news/components/preserve-scroll.js
k00b 438b5041f1 Revert "auto show: Use textarea as the anchor element if available (#2407)"
This reverts commit 1379c419df836e064d0a3f904442a7af9c4b5842.
2025-08-09 18:19:34 -05:00

54 lines
1.7 KiB
JavaScript

export default function preserveScroll (callback) {
// preserve the actual scroll position
const scrollTop = window.scrollY
// if the scroll position is at the top, we don't need to preserve it, just call the callback
if (scrollTop <= 0) {
callback()
return
}
// get a reference element at the center of the viewport to track if content is added above it
const ref = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2)
const refTop = ref ? ref.getBoundingClientRect().top + scrollTop : scrollTop
// observe the document for changes in height
const observer = new window.MutationObserver(() => {
// request animation frame to ensure the DOM is updated
window.requestAnimationFrame(() => {
// we can't proceed if we couldn't find a traceable reference element
if (!ref) {
cleanup()
return
}
// get the new position of the reference element along with the new scroll position
const newRefTop = ref ? ref.getBoundingClientRect().top + window.scrollY : window.scrollY
// has the reference element moved?
const refMoved = newRefTop - refTop
// if the reference element moved, we need to scroll to the new position
if (refMoved > 0) {
window.scrollTo({
// some browsers don't respond well to fractional scroll position, so we round up the new position to the nearest integer
top: scrollTop + Math.ceil(refMoved),
behavior: 'instant'
})
}
cleanup()
})
})
const timeout = setTimeout(() => cleanup(), 1000) // fallback
function cleanup () {
clearTimeout(timeout)
observer.disconnect()
}
observer.observe(document.body, { childList: true, subtree: true })
callback()
}