执行动态生成的Javascript

Execution of dynamically generated Javascript

我正在阅读 this 问题,接受的答案是:

Script added by setting the innerHTML property of an element doesn't get executed.

但是当我尝试更改以下代码中第一个 <script> 标记的 innerHTML 时:

<script></script>
<script>
document.querySelectorAll("script")[0].innerHTML = 'console.log("Test")';
</script>

我可以看到正在执行的 <script> 元素的注入代码(console.log() 函数输出测试)。

此外,如果我删除第一个空的 <script> 标记(从而使第一个元素 [0] 引用脚本本身),脚本会在 DOM 中更改,但是代码永远不会执行。

<script>
document.querySelectorAll("script")[0].innerHTML = 'console.log("Test")';
</script>

是什么促使了这种行为?

如果你改变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>

确实是非常有趣的发现!

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

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

以下是您的观察 example/proof,并添加了更多案例以供说明:

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 生成的 white-space,无论出于何种原因。)

正如 Laurianti 所展示的,如果脚本有一些(甚至是白色-space)内容(或src=""),它就不会工作。

此外,在“动态”示例中,请注意 innerHTMLsrc 值已更改 之后 script 元素已被更改插入到文档中。所以可以有一个空白的静态或创建动态脚本,将其留在文档中并设置在那之后很长一段时间内使用它。

抱歉没有给出完整的答案,只是想分享研究和提示。


(已删除更新;Oriol 更快、更准确。呸,很高兴看到这个问题得到解决!)

这在Scripting. When the script is being prepared

中有描述
  1. 在第 2 步中,删除了 "parser-inserted" 标志:

    If the element has its "parser-inserted" flag set, then set was-parser-inserted to true and unset the element's "parser-inserted" flag.

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

    If the element has no src attribute, and its child nodes, if any, consist only of comment nodes and empty Text nodes, then the user agent must abort these steps at this point. The script is not executed.

因此,当你修改它时,它会重新准备:

When a script element that is not marked as being "parser-inserted" experiences one of the events listed in the following list, the user agent must synchronously prepare the script element:

一旦脚本运行,修改内容将不会执行它们,因为脚本准备会中止:

If the script element is marked as having "already started", then the user agent must abort these steps at this point. The script is not executed.