Home > database >  Why does document.documentElement.scrollTop stop updating in an iframe?
Why does document.documentElement.scrollTop stop updating in an iframe?

Time:10-05

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

CodePudding user response:

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>

CodePudding user response:

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>

  • Related