最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Execution of dynamically generated Javascript - Stack Overflow

programmeradmin1浏览0评论

I was reading this question with the accepted answer being:

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

But when I try to change the innerHTML of the first <script> tag in the following code:

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

I can see the injected code for the <script> element being executed (the console.log() function outputs Test).

Furthermore if I remove the first empty <script> tag (thus making the first element [0] refer to the script itself), the script is changed in the DOM, but the code is never executed.

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

What prompts this behaviour?

I was reading this question with the accepted answer being:

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

But when I try to change the innerHTML of the first <script> tag in the following code:

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

I can see the injected code for the <script> element being executed (the console.log() function outputs Test).

Furthermore if I remove the first empty <script> tag (thus making the first element [0] refer to the script itself), the script is changed in the DOM, but the code is never executed.

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

What prompts this behaviour?

Share Improve this question edited May 23, 2017 at 12:00 CommunityBot 11 silver badge asked Sep 7, 2016 at 20:16 DanielDaniel 11.2k12 gold badges55 silver badges93 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

This is described in Scripting. When the script is being prepared,

  1. At step 2, the "parser-inserted" flag is removed:

    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. At step 4, before restoring the "parser-inserted" flag, the steps are aborted

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

Therefore, when you modify it, it will be prepared again:

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.

Once the script ran, modifying the contents won't execute them, because script preparation will abort:

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.

Very interesting finding indeed!

It really seems that empty src-less script element is in some strange state that accepts either content or even new src and interprets them. (Couldn't find a reason for that either. I just have a tiny hint: )

It resembles behavior of dynamically inserted script elements.

Here is example/proof of your observation and added few more cases for illustration:

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>

You will have to re-run snippet to see both "static" cases works.

(Also there is a blank script containing white-space generated by SO, for whatever reason.)

As Laurianti demonstrated, if the script had some (even white-space) content (or src=""), it would not work.

Also, in the "dynamic" examples notice that the innerHTML or src value is altered after the script element had been inserted to the document. So it is possible to have a blank static or create dynamic script, leave it in the document and set use it long after that.

Sorry for not giving a full answer, just wanted to share research and the hint.


(Update removed; Oriol was faster and way more accurate. Phew, glad to see this sorted out!)

If you change the innerHTML, it will not executed. You have to write it before and after you have to append it.

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

Try to adding a new line:

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

评论列表(0)

  1. 暂无评论