script tag create with innerHTML of a div doesn't work

Here is the JS code:

var wrap = document.createElement("div");
wrap.innerHTML = '<script type="text/javascript" src="'+scriptUrl+'"></script>';
var wrapscript = wrap.childNodes[0];
document.body.appendChild(wrapscript)

The body did insert the script element, but the JS resource wasn't loaded, there isn't even an http request.

Could someone explain why this is happening?

The problem is with Zeptojs's $ method

$('<script type="text/javascript" src="'+scriptUrl+'"></script>').appendTo($("bdoy"))

It works like the code above, and causes the bug.

Answers:

Answer

This one was trivial.

As stated in spec (8.4 Parsing HTML fragments and 8.2.3.5 Other parsing state flags,) quote:

when using innerHTML the browser will

  1. Create a new Document node, and mark it as being an HTML document.

  2. If there is a context element, and the Document of the context element is in quirks mode, then let the Document be in quirks mode. Otherwise, if there is a context element, and the Document of the context element is in limited-quirks mode, then let the Document be in limited-quirks mode. Otherwise, leave the Document in no-quirks mode.

  3. Create a new HTML parser, and associate it with the just created Document node. ...

and when parsing a <script> inside

The scripting flag is set to "enabled" if scripting was enabled for the Document with which the parser is associated when the parser was created, and "disabled" otherwise.

The scripting flag can be enabled even when the parser was originally created for the HTML fragment parsing algorithm, even though script elements don't execute in that case.

So it won't be executed, as long as you inject it with innerHTML.

And using innerHTML will prevent the <script> element created from being executed permanently.

As stated in spec (4.3.1 The script element,) quote:

Changing the src, type, charset, async, and defer attributes dynamically has no direct effect; these attribute are only used at specific times described below.

Concluding the described below is that, it only parse the src attribute when injecting the <script> to the document (no matter which, including the temporary one created when using innerHTML.)

So, as long as you want to inject a script to the document and make it executed, you have to use script = document.createElement('script').

Set its attributes like src and type, possibly the contents inside (by using script.appendChild(document.createTextNode(content))), then append it to the document.body.

Answer

A possible solution, when you don't have control over the insertion mechanism and you are forced to use innerHTML with script beacons, is to rebuild DOM Nodes from the "ghost" ones.

This is a recurring problem in the ad-tech industry, in a which many automated systems duplicate arbitrary HTML code (aka. adservers ^^).

works fine in Chrome:

var s = wrap.getElementsByTagName('script');
for (var i = 0; i < s.length ; i++) {
  var node=s[i], parent=node.parentElement, d = document.createElement('script');
  d.async=node.async;
  d.src=node.src;
  parent.insertBefore(d,node);
  parent.removeChild(node);
}

(you can test it in JSFiddle)

Answer

You can try this instead:

var wrap = document.createElement('div');
var scr = document.createElement('script');
scr.src = scriptUrl;
scr.type = 'text/javascript';
wrap.appendChild(scr);
document.body.appendChild(wrap);

By creating the script tag explicitly you're telling JS that the innerHTML is not a text but instead it's an executable script.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.