执行动态生成的 Javascript

Posted

技术标签:

【中文标题】执行动态生成的 Javascript【英文标题】:Execution of dynamically generated Javascript 【发布时间】:2017-01-15 15:23:29 【问题描述】:

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

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

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

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

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

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

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

是什么促使了这种行为?

【问题讨论】:

【参考方案1】:

这在Scripting 中有描述。当脚本为prepared时,

    在第 2 步,“parser-inserted”标志被移除:

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

    在第 4 步,在恢复“parser-inserted”标志之前,这些步骤被中止

    如果元素没有src属性,其子节点,如果 任何,仅包含注释节点和空的Text 节点,然后 此时用户代理必须中止这些步骤。剧本不是 执行。

因此,当你修改它时,它会再次准备:

script 元素未被标记为 "parser-inserted" 经历了以下事件之一 以下列表,用户代理必须同步prepare script元素:

script 元素得到inserted into a document,当时根据 DOM,node is inserted,在任何 同时插入的其他script元素 早在Document 中tree order。 script 元素是in a Document,一个节点或文档片段是inserted 到script 元素中, 在当时的任何script 元素inserted 之后。 script 元素是 in a Document 并且有一个 src 属性集,而以前该元素没有这样的属性 属性。

脚本运行后,修改内容不会执行,因为脚本准备会中止:

如果script 元素被标记为具有"already started",那么用户代理必须在此中止这些步骤 观点。脚本未执行。

【讨论】:

【参考方案2】:

确实非常有趣的发现!

空的 src-less 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>

您必须重新运行 sn-p 才能看到两种“静态”情况都有效。

(还有一个包含由 SO 生成的空白的空白脚本,无论出于何种原因。)

正如 Laurianti 所展示的,如果脚本有一些(甚至是空白)内容(或 src=""),它将无法工作。

此外,在“动态”示例中,请注意innerHTMLsrc 值在script 元素插入文档后 发生了更改。因此,可以有一个空白的静态或创建动态脚本,将其留在文档中并设置使用它很久之后。

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


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

【讨论】:

【参考方案3】:

如果你改变了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 时,我的脚本正在执行,这是我问题的全部重点 - 为什么会发生这种情况? 因为它是空的。我不知道为什么。 因为您正在编写自执行代码行,而不是必须单独调用的变量或函数。

以上是关于执行动态生成的 Javascript的主要内容,如果未能解决你的问题,请参考以下文章

HtmlUnit爬取Ajax动态生成的页面内容

如何使用javascript验证动态生成的输入框值

Javascript未从动态生成的表单提交数据

JavaScript如何在div中动态生成<p>

如何为所有动态生成的 div 添加常用的 Javascript 函数? [复制]

在 Javascript 中动态修改或生成代码