在 Web 扩展中,我一直将模板元素从后台页面作为字符串传递到空的本地 HTML 文件,并使用
browser.tabs.insertCSS
和 browser.tabs.executeScript
来传递 CSS 和 JS。这一直运行良好,但我最近了解到 fetch 可用于直接从扩展中检索文件,这将减少后台页面的大小或完全消除它,只留下后台脚本并减少 RAM 使用量。
我在这个SO问题中看到了一些例子,使用
innerHTML
将HTML字符串写入body元素,还有一个使用DOMParser.
,但是如果你尝试一下,HTML就会添加到body元素中。
另一个SO问题,十岁了,有一个五岁的最后一个回答,这对于像我这样的新手来说很有趣;我尝试了这个,似乎有效。
fetch('file.html')
.then( response => response.text() )
.then( result => {
let parser = new DOMParser(),
doc = parser.parseFromString( result, 'text/html' );
document.replaceChild( doc.documentElement, document.documentElement );
} );
我的问题是,这是一种有效且可靠的完全替换 html 元素的方法吗?
谢谢你。
不。在某些情况下,这会产生意想不到的副作用。
这是一个简单的例子来演示这一点:
index.html
<html>
<head>
<title>DOM Parser test</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>This is the original file</h1>
<button id="btn1" type="button">Click to reload</button>
<script>
var btn1 = document.getElementById('btn1');
function onClick() {
fetch('file.html')
.then( response => response.text() )
.then( result => {
let parser = new DOMParser();
doc = parser.parseFromString( result, 'text/html' );
document.replaceChild( doc.documentElement, document.documentElement );
} );
}
btn1.addEventListener('click', onClick);
</script>
</body>
</html>
文件.html
<html>
<head>
<title>DOM Parser test</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>This is the second file</h1>
<button id="btn1" type="button">Click to reload</button>
<script>
var btn1 = document.getElementById('btn1');
function onClick() {
fetch('file.html')
.then( response => response.text() )
.then( result => {
let parser = new DOMParser();
doc = parser.parseFromString( result, 'text/html' );
document.replaceChild( doc.documentElement, document.documentElement );
} );
}
btn1.addEventListener('click', onClick);
</script>
</body>
</html>
样式.css
html body {
background: #00003f;
}
我使用 Node JS 提供这些静态文件
http-server ./
并访问了index.html
现在单击“单击重新加载”按钮。这应该获取 file.html 并渲染它。
在index.html和file.html的源代码中可以看到,它们都引用了style.css样式表。 index.html 呈现引用的样式,而获取并用于更新 documentElement 的 file.html 不遵循样式表。
这是事物如何崩溃的一个例子。
自从我问这个问题以来已经有一段时间了,我几乎不明白来问它。从那时起我学到了一些东西并认为这是可能的。我并不是说这是专业的方法,甚至不是一个好主意,但它对我的项目效果很好。
请不要认为我写这篇文章是因为我认为我发现了一些新的或新颖的东西。我写它是因为,如果我在问这个问题时就知道这一点,我就可以更轻松地实现我的目标。
下面的
rspObj.data
是HTML文档文本,但是它是如何检索的,例如通过fetch
,不包括其代码。
问题是脚本未执行且样式表未应用,即使它们出现在控制台中就像已加载一样。如果将样式表添加到
head
,并将 script
元素添加到 body
及其属性集,两者都作为单独的步骤,那么它们将被接受并执行。
以前的脚本似乎保留在开发人员工具的源列表中,但新文档似乎无法再次使用它们。这可能是因为我使用的是模块;我不确定,但我无法调用新文档模块中的函数,即使它是由先前的模块导出和导入的。但是,如果再次加载脚本,那么一切都会像加载新页面一样工作。
我并不是说这是一个很好的做法,但问题就好像这都是神秘的无意副作用,而不是与解析文档时样式表和脚本的执行方式有关。这似乎与一些非常古老的问题没有什么不同,这里涉及如何运行从
XMLHttpRequest
检索的脚本。
let
d = new DOMParser(),
b = d.parseFromString( rspObj.data, 'text/html' ),
src = [],
c;
if ( (c = document.querySelector("head")).replaceChildren ) {
// Not supported in olders version of Safari.
c.replaceChildren( ...b.head.children );
} else {
c.parentNode.replaceChild( b.head, c );
}
b.querySelectorAll("script").forEach( v => {
src.push(v.src); // Or it could be textContent.
v.remove();
});
document.documentElement.replaceChild( b.body, document.body );
src.forEach( v => {
(c = document.createElement("SCRIPT")).src = v;
c.type = "module";
document.body.appendChild(c);
});