通过删除 `<script type="text/plain"... />` 的类型属性延迟脚本加载不适用于 firefox

问题描述 投票:0回答:1

我目前正在为 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
.

javascript html dom firefox browser
1个回答
0
投票

有趣的是,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);
}
© www.soinside.com 2019 - 2024. All rights reserved.