如何在加载后从缓存中读取 iframe 的*原始*源
How to read the *original* source of an iframe, after it has loaded, from the cache
最通用的描述方式是我只想触发一个网络请求,只有在客户端看到视口的某个部分后,然后使用它并在 iframe 中显示它,在大多数情况下可能的有效方式。
给定一个 DOM 结构如下:
<!DOCTYPE html>
<html>
<head />
<body>
...
<iframe loading="lazy" sandbox="" src="http://www.example.com" />
<pre />
...
</body>
</html>
我想在 pre
标签中向客户展示上面 iframe
的来源是什么样子的。
iframe
元素可以承载任意文档,它可以是文本的或二进制的,所有已知的是浏览器可以显示它。
iframe 的来源 URL 托管在同一来源。
我的目标是通过 Chromium“view-source:”URL 或类似的方式显示人们会看到的内容。
可能无法访问 .contentWindow
或 .contentdocument
属性,因为它完全是沙盒化的,但即使我可以,文档的 outerHTML
也不够,并且使用XMLSerializer
显然会改变输出。此外,我相信浏览器可以编辑文档的某些区域,例如不必要的空格或格式设置。
我只是尝试了以下方法:
document
.body
.querySelector("iframe")
.addEventListener(
"load",
async ( { currentTarget: { src } } ) => {
const data = await fetch(
src, {
cache: "only-if-cached"
}
);
// ... use data
}, {
passive: true,
once: true
}
);
然而,提取失败。 URL 似乎不在浏览器的缓存中,但我不想发起新的网络请求,有没有一种有效的方法可以做到这一点?
我正在考虑使用 Intersection Observer 作为一个潜在的解决方案,因为它只会导致一个网络请求,但代码很长,而且似乎没有正常工作(我对观察者没有经验).
事件监听器和提取请求可能会发生一些事情。其他读者请注意,截至撰写本文时(2020 年 11 月),仅 Chrome/Chromium、Edge 和 Opera 支持 iframe 中的 lazy loading。
此回答针对的是想同时使用延迟加载来动态加载内容的提问者。如果问题只是关于发出单个网络请求,则可以通过获取请求并通过 Blob 或数据 URI 或通过 srcdoc
设置 iframe 的 src
或使用 Service Workers(已探索)来完成下面一点)。
在添加事件侦听器之前可能加载的帧
您在框架声明后向其添加事件侦听器,如果框架在脚本被评估之前加载(如果它被缓存,则可能会出现这种情况),这可能会产生问题。
通常,您可以通过 contentWindow
或 contentDocument
属性 查看您的框架是否已经加载,如果是 运行 您在这些框架上的初始化代码,但由于框架是沙盒化的,因此无法访问这些属性。您可以做的是提前声明您的处理程序,然后在创建 iframe 时声明它:
async function loader(frame) {
console.log(frame.src);
// ...
}
<iframe src="frame.html" loading="lazy" sandbox="" onload="loader(this)"></iframe>
在获取请求中使用同源
According to MDN,only-if-cached
只能在mode
设置为same-origin
时使用同源。提取请求看起来像:
await fetch(src, { mode: 'same-origin', cache: 'only-if-cached' });
结果
main.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<style>
.spacer {
height: 200vh;
background: lightgray;
text-align: center;
}
</style>
<script>
async function loader(frame) {
var sourceCodeElementId = frame.dataset.code;
var sourceCodeElement = document.getElementById(sourceCodeElementId);
var response = await fetch(frame.src, {
mode: 'same-origin',
cache: 'only-if-cached'
});
if (response.status === 504) {
// if it didn't load via cache, just get it normally
response = await fetch(frame.src);
}
var text = await response.text();
sourceCodeElement.textContent = text;
}
</script>
</head>
<body>
<div class="spacer">Scroll Down</div>
<iframe src="frame.html"
loading="lazy"
data-code="frame-source-code"
sandbox=""
onload="loader(this)">
</iframe>
<pre id="frame-source-code"></pre>
</body>
</html>
样本frame.html
<!doctype html>
<html>
Frame
</html>
Optionally/Alternatively 使用 Service Worker
如果您担心的是网络请求,您可以使用 Service Workers, alternatively, or additionally to the above fixes. It takes a bit of work to get them up and running, but they can give you a finer control over network requests. Caching files 是一个常见的教程,因此如果您采用该路线,您应该能够找到相当数量的资源。
最通用的描述方式是我只想触发一个网络请求,只有在客户端看到视口的某个部分后,然后使用它并在 iframe 中显示它,在大多数情况下可能的有效方式。
给定一个 DOM 结构如下:
<!DOCTYPE html>
<html>
<head />
<body>
...
<iframe loading="lazy" sandbox="" src="http://www.example.com" />
<pre />
...
</body>
</html>
我想在 pre
标签中向客户展示上面 iframe
的来源是什么样子的。
iframe
元素可以承载任意文档,它可以是文本的或二进制的,所有已知的是浏览器可以显示它。
iframe 的来源 URL 托管在同一来源。
我的目标是通过 Chromium“view-source:”URL 或类似的方式显示人们会看到的内容。
可能无法访问 .contentWindow
或 .contentdocument
属性,因为它完全是沙盒化的,但即使我可以,文档的 outerHTML
也不够,并且使用XMLSerializer
显然会改变输出。此外,我相信浏览器可以编辑文档的某些区域,例如不必要的空格或格式设置。
我只是尝试了以下方法:
document
.body
.querySelector("iframe")
.addEventListener(
"load",
async ( { currentTarget: { src } } ) => {
const data = await fetch(
src, {
cache: "only-if-cached"
}
);
// ... use data
}, {
passive: true,
once: true
}
);
然而,提取失败。 URL 似乎不在浏览器的缓存中,但我不想发起新的网络请求,有没有一种有效的方法可以做到这一点?
我正在考虑使用 Intersection Observer 作为一个潜在的解决方案,因为它只会导致一个网络请求,但代码很长,而且似乎没有正常工作(我对观察者没有经验).
事件监听器和提取请求可能会发生一些事情。其他读者请注意,截至撰写本文时(2020 年 11 月),仅 Chrome/Chromium、Edge 和 Opera 支持 iframe 中的 lazy loading。
此回答针对的是想同时使用延迟加载来动态加载内容的提问者。如果问题只是关于发出单个网络请求,则可以通过获取请求并通过 Blob 或数据 URI 或通过 srcdoc
设置 iframe 的 src
或使用 Service Workers(已探索)来完成下面一点)。
在添加事件侦听器之前可能加载的帧
您在框架声明后向其添加事件侦听器,如果框架在脚本被评估之前加载(如果它被缓存,则可能会出现这种情况),这可能会产生问题。
通常,您可以通过 contentWindow
或 contentDocument
属性 查看您的框架是否已经加载,如果是 运行 您在这些框架上的初始化代码,但由于框架是沙盒化的,因此无法访问这些属性。您可以做的是提前声明您的处理程序,然后在创建 iframe 时声明它:
async function loader(frame) {
console.log(frame.src);
// ...
}
<iframe src="frame.html" loading="lazy" sandbox="" onload="loader(this)"></iframe>
在获取请求中使用同源
According to MDN,only-if-cached
只能在mode
设置为same-origin
时使用同源。提取请求看起来像:
await fetch(src, { mode: 'same-origin', cache: 'only-if-cached' });
结果
main.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<style>
.spacer {
height: 200vh;
background: lightgray;
text-align: center;
}
</style>
<script>
async function loader(frame) {
var sourceCodeElementId = frame.dataset.code;
var sourceCodeElement = document.getElementById(sourceCodeElementId);
var response = await fetch(frame.src, {
mode: 'same-origin',
cache: 'only-if-cached'
});
if (response.status === 504) {
// if it didn't load via cache, just get it normally
response = await fetch(frame.src);
}
var text = await response.text();
sourceCodeElement.textContent = text;
}
</script>
</head>
<body>
<div class="spacer">Scroll Down</div>
<iframe src="frame.html"
loading="lazy"
data-code="frame-source-code"
sandbox=""
onload="loader(this)">
</iframe>
<pre id="frame-source-code"></pre>
</body>
</html>
样本frame.html
<!doctype html>
<html>
Frame
</html>
Optionally/Alternatively 使用 Service Worker
如果您担心的是网络请求,您可以使用 Service Workers, alternatively, or additionally to the above fixes. It takes a bit of work to get them up and running, but they can give you a finer control over network requests. Caching files 是一个常见的教程,因此如果您采用该路线,您应该能够找到相当数量的资源。