0

Here's some code showing document.documentElement.scrollTop as you scroll

const contentElem = document.querySelector('#content')
for (let i = 0; i < 500; ++i) {
  const elem = document.createElement('h1');
  elem.textContent = `line: ${i}`;
  contentElem.appendChild(elem);
}

const debugElem = document.querySelector('#debug');
function update() {
  debugElem.textContent = `scrollY: ${window.scrollY}
documentElement.scrollTop: ${document.documentElement.scrollTop}
documentElement.nodeName: ${document.documentElement.nodeName}`;
  requestAnimationFrame(update);
}
requestAnimationFrame(update);
#under {
  position: absolute;
  transform: translateY(500px);
  left: 0;
  top: 0;
  background-color: pink;
  width: 100%;
  height: 100%;
  z-index: -1;
}
#debug {
  position: fixed;
  color: white;
  left: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.8);
}
<div id="under"></div>
<div id="content"></div>
<pre id="debug"></pre>

And here is the exact same code running in an iframe.

const script = 'script';
const html = `
<style>
#under {
  position: absolute;
  transform: translateY(500px);
  left: 0;
  top: 0;
  background-color: pink;
  width: 100%;
  height: 100%;
  z-index: -1;
}
#debug {
  position: fixed;
  color: white;
  left: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.8);
}
</style>
<div id="under"></div>
<div id="content"></div>
<pre id="debug"></pre>
<${script}>
const contentElem = document.querySelector('#content')
for (let i = 0; i < 500; ++i) {
  const elem = document.createElement('h1');
  elem.textContent = \`line: \${i}\`;
  contentElem.appendChild(elem);
}

const debugElem = document.querySelector('#debug');
function update() {
  debugElem.textContent = \`scrollY: \${window.scrollY}
documentElement.scrollTop: \${document.documentElement.scrollTop}
documentElement.nodeName: \${document.documentElement.nodeName}\`;
  requestAnimationFrame(update);
}
requestAnimationFrame(update);
</${script}>
`;

const iframeElem = document.querySelector('iframe');
iframeElem.src = URL.createObjectURL(new Blob([html], {type: 'text/html'}));
<iframe></iframe>

Inside the iframe, the value document.documentElement.scrollTop never changes. Why? Note: Tested on Chrome, Firefox, and Safari

gman
  • 100,619
  • 31
  • 269
  • 393

2 Answers2

1

It seems that documentElement.scrollTop: \${document.body.scrollTop} works better in that case, thanks to this answer discussion.

const script = 'script';
const html = `
<style>
#under {
  position: absolute;
  transform: translateY(500px);
  left: 0;
  top: 0;
  background-color: pink;
  width: 100%;
  height: 100%;
  z-index: -1;
}
#debug {
  position: fixed;
  color: white;
  left: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.8);
}
</style>
<div id="under"></div>
<div id="content"></div>
<pre id="debug"></pre>
<${script}>
const contentElem = document.querySelector('#content')
for (let i = 0; i < 500; ++i) {
  const elem = document.createElement('h1');
  elem.textContent = \`line: \${i}\`;
  contentElem.appendChild(elem);
}

const debugElem = document.querySelector('#debug');
function update() {
  debugElem.textContent = \`scrollY: \${window.scrollY}
documentElement.scrollTop: \${document.body.scrollTop}
documentElement.nodeName: \${document.documentElement.nodeName}\`;
  requestAnimationFrame(update);
}
requestAnimationFrame(update);
</${script}>
`;

const iframeElem = document.querySelector('iframe');
iframeElem.src = URL.createObjectURL(new Blob([html], {type: 'text/html'}));
<iframe></iframe>
Philippe
  • 1,134
  • 12
  • 22
  • Thank you for the workaround. Still, it's not an answer of "why". Given the behavior is the same on all browsers the fact that document.documentElement.scrollTop is not working must have a reason. – gman Oct 04 '22 at 21:30
  • @gman you're right, this answer does not explain the reason. The only approch I could figure would be that an iframe is not at the same level of the html document hierarchy,, it's a kind of nested object. So I'm not surprised if some things differ. Maybe I'm wrong ;) – Philippe Oct 04 '22 at 21:45
1

The issue is not related to iframe, as you may note your working example also runs in an iframe. It is about the content of the iframe, specifically the document quirks. You may note the following warning in the console when the iframe is loaded:

This page is in Quirks Mode. Page layout may be impacted. For Standards Mode use “<!DOCTYPE html>”.

Thus, the scrollTop in this case behaves according to the following rule of the specs:

  1. If the element is the root element and document is in quirks mode, return zero and terminate these steps.

To make it work, the html must be formatted with the proper DOCTYPE:

const script = 'script';
const html = `
<!DOCTYPE html>
<style>
#under {
  position: absolute;
  transform: translateY(500px);
  left: 0;
  top: 0;
  background-color: pink;
  width: 100%;
  height: 100%;
  z-index: -1;
}
#debug {
  position: fixed;
  color: white;
  left: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.8);
}
</style>
<div id="under"></div>
<div id="content"></div>
<pre id="debug"></pre>
<${script}>
const contentElem = document.querySelector('#content')
for (let i = 0; i < 500; ++i) {
  const elem = document.createElement('h1');
  elem.textContent = \`line: \${i}\`;
  contentElem.appendChild(elem);
}

const debugElem = document.querySelector('#debug');
function update() {
  debugElem.textContent = \`scrollY: \${window.scrollY}
documentElement.scrollTop: \${document.documentElement.scrollTop}
documentElement.nodeName: \${document.documentElement.nodeName}\`;
  requestAnimationFrame(update);
}
requestAnimationFrame(update);
</${script}>
`;

const iframeElem = document.querySelector('iframe');
iframeElem.src = URL.createObjectURL(new Blob([html], {type: 'text/html'}));
<iframe></iframe>
n--
  • 3,563
  • 4
  • 9
  • Well, meh..... I was actually asking this question to try to find a lead to different but related issue. In that other issue (don't have a a minimal repo yet), both `window.scrollY` and `documentElement.scrollTop` stop working. But that page has ` ` everywhere (main, iframe, child iframe) so I guess it's a different issue. I'll have to try to whittle it down. – gman Oct 04 '22 at 23:25