我目前正在为 NextJS 开发自定义 cookie 同意解决方案,它似乎已经很好地适用于这种方法:
head中所有需要同意后加载的脚本都是这样实现的:
<script src="iSetSomeCookies.js" type="text/plain" data-cookieconsent />
type="text/plain"
阻止浏览器加载和执行脚本。
同意后,我去掉type
属性,克隆节点,删除原来的<script>
节点,追加到<head>
。否则浏览器不会注意到变化。
此方法适用于 Safari 和 Chrome,但不适用于 Firefox ... 代码被执行,
type
属性也被删除并添加为新的脚本节点。但是在网络选项卡中,脚本不会在之后加载。
有没有办法让 Firefox 重新解析 DOM 并加载脚本?
这是在获得 cookie 同意后执行的脚本部分:
const blockedScripts = document.querySelectorAll<HTMLScriptElement>(
"script[data-cookieconsent]"
)
blockedScripts.forEach((script) => {
script.removeAttribute("type")
const copy = script.cloneNode()
document.head.insertBefore(copy, script)
script.remove()
})
我也尝试将类型更改为
text/javascript
和 application/javascript
但这仍然是相同的行为,Firefox 似乎忽略了这种变化。
我已经看到一些解决方案可以与
eval(this.innerHtml)
一起使用以在之后执行脚本,但我不想使用 eval
.
有趣的是,Firefox 不会运行克隆脚本引用的代码(我可以验证我在您的原始代码中看到了这种行为)。我认为最简单的事情就是创建新节点,这似乎可行:
const blockedScripts = document.querySelectorAll("script[data-cookieconsent]");
for (const blocked of blockedScripts) {
const { parentElement, nextSibling } = blocked;
blocked.remove();
const script = document.createElement("script");
for (const attr of blocked.attributes) {
if (attr.name !== "type") {
script.setAttributeNode(attr.cloneNode());
}
}
parentElement.insertBefore(script, nextSibling);
}
该代码也不假定
script
元素在 head
元素中;相反,它使用原始元素的任何父元素。
这是在 TypeScript 中(由于
as Attr
的返回类型是 cloneNode
,因此在克隆属性后必须添加 Node
,并添加一个 if
以说服 TypeScript parentElement
不是 null
:
const blockedScripts = document.querySelectorAll<HTMLScriptElement>("script[data-cookieconsent]");
for (const blocked of blockedScripts) {
const { parentElement, nextSibling } = blocked;
if (!parentElement) { // Will never be true
throw new Error(`parentElement is null`);
}
blocked.remove();
const script = document.createElement("script");
for (const attr of blocked.attributes) {
if (attr.name !== "type") {
script.setAttributeNode(attr.cloneNode() as Attr);
}
}
parentElement.insertBefore(script, nextSibling);
}
您可能更喜欢
parentElement
上的非空断言运算符;我真的不喜欢使用它,但这里有一个很好的论据:
const blockedScripts = document.querySelectorAll<HTMLScriptElement>("script[data-cookieconsent]");
for (const blocked of blockedScripts) {
const { parentElement, nextSibling } = blocked;
blocked.remove();
const script = document.createElement("script");
for (const attr of blocked.attributes) {
if (attr.name !== "type") {
script.setAttributeNode(attr.cloneNode() as Attr);
}
}
parentElement!.insertBefore(script, nextSibling);
}