动态生成Javascript的执行

6
我正在阅读这个问题,接受的答案是:这里

通过设置元素的innerHTML属性添加的脚本不会被执行。

但是当我尝试更改以下代码中第一个<script>标签的innerHTML时:
<script></script>
<script>
document.querySelectorAll("script")[0].innerHTML = 'console.log("Test")';
</script>

我可以看到为<script>元素注入的代码被执行了(console.log()函数输出Test)。
此外,如果我删除第一个空的<script>标签(使得第一个元素[0]指向脚本本身),虽然脚本在DOM中已经改变了,但是代码并没有被执行。
<script>
document.querySelectorAll("script")[0].innerHTML = 'console.log("Test")';
</script>

是什么促使了这种行为?

3个回答

4
这在脚本编写中有所描述。当脚本正在准备时,
  1. 在第2步中,"parser-inserted"标志被移除:

    如果该元素的"parser-inserted"标志被设置,则将was-parser-inserted设置为true,并取消该元素的"parser-inserted"标志。

  2. 在第4步之前恢复"parser-inserted"标志时,步骤会被中止

    如果该元素没有src属性,且其子节点(如果有)仅由注释节点和空的Text节点组成,则用户代理必须在此时中止这些步骤。脚本不会执行。

因此,当您进行修改时,它将再次准备好:
当一个没有标记为“parser-inserted”的script元素遇到以下列表中列出的事件之一时,用户代理必须同步准备script元素:
  • 插入一个script元素到文档中,在DOM中按照节点插入的时间,在同一时间插入的先前在Document中的任何其他script元素之后。
  • script元素Document中,并且在此期间插入了节点或文档片段到script元素中,在任何同时插入的script元素之后。
  • script元素Document中,并且设置了先前不存在该属性的src属性。
一旦脚本运行,修改内容将不会执行它们,因为脚本准备将中止:

如果script元素被标记为已经启动,则用户代理必须在此时中止这些步骤。该脚本不会被执行。


2

真是一个非常有趣的发现!

空的没有src属性的 script 元素似乎处于某种奇怪的状态,可以接受内容甚至新的 src 并解释它们。(我也找不到原因。我只有一个小提示:)

它类似于动态插入脚本元素的行为。

这里是你的观察证明的例子,并添加了几个更多的案例以进行说明:

script[src]::before,
script {
  display: block;
  border: 1px solid red;
  padding: 1em;
}
script[src]::before,
script {
  content: 'src='attr(src)
}
button {
  display: block
}
<p>Existing empty script in HTML:</p>
<script id="existing"></script>
<p>Can be invoked just one of:</p>
<button onclick="eval(this.innerText)">
  existing.innerHTML='console.log("innerHTML to static")'
</button>
<button onclick="eval(this.innerText)">
  existing.src='data:text/javascript,console.log("src to static")'
</button>
<p>Dynamically created and inserted script (each creates own, so both work):</p>
<button onclick="eval(this.innerText)">
  document.body.appendChild(document.createElement('script')).innerHTML='console.log("innerHTML to dynamic")'
</button>
<button onclick="eval(this.innerText)">
  document.body.appendChild(document.createElement('script')).src='data:text/javascript,console.log("src to dynamic")'
</button>

您需要重新运行代码片段才能查看两个“静态”案例的效果。
(此外,由于某种原因,SO生成了一个包含空格的空脚本。)
正如Laurianti所示,如果脚本有一些(甚至是空格)内容(或src =“”),它将无法工作。
另外,在“动态”示例中,请注意在向文档插入script元素之后更改innerHTMLsrc值。因此,可以创建一个空的静态或动态脚本,将其留在文档中并在此之后长时间使用。
很抱歉没有给出完整的答案,只是想分享研究和提示。
(更新已删除;Oriol更快且更准确。呼!很高兴看到这个问题得到解决!)

0
如果你改变innerHTML,它将不会被执行。你必须在写之前和之后附加它。

    var script = document.createElement("script");
    script.innerHTML = 'console.log("Test");';
    document.getElementsByTagName('head')[0].appendChild(script);

尝试添加一个新行:
<script>
</script>
<script>
document.querySelectorAll("script")[0].innerHTML = 'console.log("Test")';
</script>

但是当我更改innerHTML时,我的脚本正在执行,这正是我提问的全部意义 - 这是为什么? - Daniel
因为它是空的。我不知道为什么。 - Laurianti
因为您正在编写一个自执行的代码行,而不是必须单独调用的变量或函数。 - Running Buffalo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接