执行动态生成的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=""
),它就不会工作。
此外,在“动态”示例中,请注意 innerHTML
或 src
值已更改 在 之后 script
元素已被更改插入到文档中。所以可以有一个空白的静态或创建动态脚本,将其留在文档中并设置在那之后很长一段时间内使用它。
抱歉没有给出完整的答案,只是想分享研究和提示。
(已删除更新;Oriol 更快、更准确。呸,很高兴看到这个问题得到解决!)
这在Scripting. When the script is being prepared、
中有描述
在第 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.
在第 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:
- The
script
element gets inserted into a document, at the time the node is inserted according to the DOM, after any
other script
elements inserted at the same time that are
earlier in the Document
in tree order.
- The
script
element is in a Document
and a node or document fragment is inserted into the script
element,
after any script
elements inserted at that time.
- The
script
element is in a Document
and has a src
attribute set where previously the element had no such
attribute.
一旦脚本运行,修改内容将不会执行它们,因为脚本准备会中止:
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.
我正在阅读 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=""
),它就不会工作。
此外,在“动态”示例中,请注意 innerHTML
或 src
值已更改 在 之后 script
元素已被更改插入到文档中。所以可以有一个空白的静态或创建动态脚本,将其留在文档中并设置在那之后很长一段时间内使用它。
抱歉没有给出完整的答案,只是想分享研究和提示。
(已删除更新;Oriol 更快、更准确。呸,很高兴看到这个问题得到解决!)
这在Scripting. When the script is being prepared、
中有描述在第 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.
在第 4 步,在恢复 "parser-inserted" 标志之前,步骤被中止
If the element has no
src
attribute, and its child nodes, if any, consist only of comment nodes and emptyText
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 thescript
element:
- The
script
element gets inserted into a document, at the time the node is inserted according to the DOM, after any otherscript
elements inserted at the same time that are earlier in theDocument
in tree order.- The
script
element is in aDocument
and a node or document fragment is inserted into thescript
element, after anyscript
elements inserted at that time.- The
script
element is in aDocument
and has asrc
attribute set where previously the element had no such attribute.
一旦脚本运行,修改内容将不会执行它们,因为脚本准备会中止:
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.