Tools: How to Get the Correct Y Position of an Element in CHATGPT UI (2026)
The Problem
Why window.scrollY Is Always 0
Step 1: Target the Element
Step 2: Find the Scroll Container
Step 3: Calculate the Correct Y Position.
How to verify the Y Value is Correct
Test 1: Scroll to it
Test 2: Visual Marker
Test 3: Reverse Check
What Does NOT Work (and Why)
Key Takeaways
Final snippet
When This Pattern Is Useful You try to get the vertical position of an element like this: ...but it always returns 0. Even worse, this common pattern also fails: If you're inspecting the ChatGPT web UI, this can be very confusing. In ChatGPT (and many modern web apps): So "the top of the page" is not the document - it's the scrollable container. This helper walks up the DOM and finds the element that actually scrolls: This gives you the distance from the beginning of the scrollable page: If the value is correct, the element will align perfectly: Draw a horizontal line at the calculated Y: Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or
window.scrollY CODE_BLOCK: window.scrollY CODE_BLOCK: window.scrollY CODE_BLOCK: element.getBoundingClientRect().top + window.scrollY CODE_BLOCK: element.getBoundingClientRect().top + window.scrollY CODE_BLOCK: element.getBoundingClientRect().top + window.scrollY CODE_BLOCK: window.scrollY === 0 // always true CODE_BLOCK: window.scrollY === 0 // always true CODE_BLOCK: window.scrollY === 0 // always true CODE_BLOCK: const el = document.querySelector("div.whitespace-pre-wrap"); CODE_BLOCK: const el = document.querySelector("div.whitespace-pre-wrap"); CODE_BLOCK: const el = document.querySelector("div.whitespace-pre-wrap"); CODE_BLOCK: function findScrollParent(element) { let parent = element.parentElement; while (parent) { const style = getComputedStyle(parent); if (/(auto|scroll)/.test(style.overflowY)) { return parent; } parent = parent.parentElement; } return document.documentElement; } const container = findScrollParent(el); CODE_BLOCK: function findScrollParent(element) { let parent = element.parentElement; while (parent) { const style = getComputedStyle(parent); if (/(auto|scroll)/.test(style.overflowY)) { return parent; } parent = parent.parentElement; } return document.documentElement; } const container = findScrollParent(el); CODE_BLOCK: function findScrollParent(element) { let parent = element.parentElement; while (parent) { const style = getComputedStyle(parent); if (/(auto|scroll)/.test(style.overflowY)) { return parent; } parent = parent.parentElement; } return document.documentElement; } const container = findScrollParent(el); CODE_BLOCK: const y = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop; console.log(y); CODE_BLOCK: const y = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop; console.log(y); CODE_BLOCK: const y = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop; console.log(y); CODE_BLOCK: container.scrollTo({ top: y, behavior: "smooth" }); CODE_BLOCK: container.scrollTo({ top: y, behavior: "smooth" }); CODE_BLOCK: container.scrollTo({ top: y, behavior: "smooth" }); CODE_BLOCK: const marker = document.createElement("div"); marker.style.position = "absolute"; marker.style.left = "0"; marker.style.right = "0"; marker.style.background = "red"; marker.style.height = "2px"; marker.style.top = y + "px"; document.body.appendChild(marker); CODE_BLOCK: const marker = document.createElement("div"); marker.style.position = "absolute"; marker.style.left = "0"; marker.style.right = "0"; marker.style.background = "red"; marker.style.height = "2px"; marker.style.top = y + "px"; document.body.appendChild(marker); CODE_BLOCK: const marker = document.createElement("div"); marker.style.position = "absolute"; marker.style.left = "0"; marker.style.right = "0"; marker.style.background = "red"; marker.style.height = "2px"; marker.style.top = y + "px"; document.body.appendChild(marker); CODE_BLOCK: container.scrollTop = y; const diff = el.getBoundingClientRect().top() - container.getBoudningClientRect().top; console.log(diff); CODE_BLOCK: container.scrollTop = y; const diff = el.getBoundingClientRect().top() - container.getBoudningClientRect().top; console.log(diff); CODE_BLOCK: container.scrollTop = y; const diff = el.getBoundingClientRect().top() - container.getBoudningClientRect().top; console.log(diff); CODE_BLOCK: window.scrollY // page doesn't scroll document.body.scrollTop element.offsetTop // breaks with nested layouts CODE_BLOCK: window.scrollY // page doesn't scroll document.body.scrollTop element.offsetTop // breaks with nested layouts CODE_BLOCK: window.scrollY // page doesn't scroll document.body.scrollTop element.offsetTop // breaks with nested layouts CODE_BLOCK: const el = document.querySelector("div.whitespace-pre-wrap"); const container = findScrollParent(el); const y = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop; CODE_BLOCK: const el = document.querySelector("div.whitespace-pre-wrap"); const container = findScrollParent(el); const y = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop; CODE_BLOCK: const el = document.querySelector("div.whitespace-pre-wrap"); const container = findScrollParent(el); const y = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop;
- The page itself does not scroll
- Scrolling happens inside a nested container
- The <body> is fixed-height - window.scrollY only works when the document scrolls
- ChatGPT uses an internal scroll container
- Always compute positions relative to the scroll container
- Use getBoundingClientRect + scrollTop for reliable results
- Browser extensions
- UI automation (Playwright / Selenium)
- Auto-scrolling chat UIs
- Virtualized lists
- Any SPA where windows.scrollY is useless