An external source is providing the content of the script tags written as HTML in a string. I need to add these script tags into the head. If I do it like this, all script tags are added to the DOM in head tag, however none of the sources are actually being loaded (devtools network tab).
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>
let sLoadThis = '<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/underscore@latest/underscore-umd-min.js">'
sLoadThis = sLoadThis + "<" + "/script>"
let oScript = $(sLoadThis).get(0);
document.head.appendChild(oScript);
</script>
</body>
</html>
If I sort of use the jQuery as an interpreter to then insert the script tag with vanilla JS, it works:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>
let sLoadThis = '<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/underscore@latest/underscore-umd-min.js">'
sLoadThis = sLoadThis + "<" + "/script>"
let oScript = $(sLoadThis).get(0);
var vanillaScript = document.createElement('script')
vanillaScript .type = 'text/javascript'
vanillaScript .src = oScript.src
document.head.appendChild(vanillaScript )
</script>
</body>
</html>
I would like to understand why it doesn't work in my first example.
The second example runs because you 're using a dom node. That 's how appendChild() works. The jQuery get() method works with existing dom elements. The only thing you 've got is a string, that does not exist in your dom yet. Try the following.
let script = $.parseHtml(sLoadThis);
console.log(script.get(0)); // should be a script dom node element
document.head.appendChild(script.get(0));
In vanilla JavaScript its even easier and much more faster than jQuery because you don 't have to use a dependency.
const fragment = document.createRange().createContextualFragment(htmlStr);
document.head.appendChild(fragment);
As far as I know there are 4 ways to create a DOM element from a string:
HTML5 specifies that a <script> tag inserted with one of the 3 first options should not execute because it could became a security risk. See this
So, in the example, although all script tags are added to the DOM in the <head> section, only the option number 4 will be executed.
const str1 = "<script>alert('option 1 executed')" + "<" + "/script>"
const str2 = "<script>alert('option 2 executed')" + "<" + "/script>"
const str3 = "<script>alert('option 3 executed')" + "<" + "/script>"
const str4 = "<script>alert('option 4 executed')" + "<" + "/script>"
// OPTION 1
const placeholder1 = document.createElement('div')
placeholder1.innerHTML = str1
const node1 = placeholder1.firstChild
document.head.appendChild(node1)
// OPTION 2
const placeholder2 = document.createElement('div')
placeholder2.insertAdjacentHTML('afterbegin', str2)
const node2 = placeholder2.firstChild
document.head.appendChild(node2)
// OPTION 3
const node3 = new DOMParser().parseFromString(str3, 'text/html').head.firstElementChild
document.head.appendChild(node3)
// OPTION 4
const node4 = document.createRange().createContextualFragment(str4)
document.head.appendChild(node4)