233

I want to dynamically include a script tag in a webpage however I have no control of it's src so src="source.js" may look like this.

document.write('<script type="text/javascript">')
document.write('alert("hello world")')
document.write('</script>')
document.write('<p>goodbye world</p>')

Now ordinarily putting

<script type="text/javascript" src="source.js"></script>

In the head works fine but is there any other way I can add source.js dynamically using something like innerHTML?

jsfiddle of what i've tried

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
Cadell Christo
  • 3,105
  • 3
  • 21
  • 19
  • 4
    after hours of struggling postscribe is the solution! [https://github.com/krux/postscribe/](https://github.com/krux/postscribe/) – Cadell Christo Oct 29 '12 at 16:02
  • 1
    Possible duplicate of [async loading javascript with document.write](http://stackoverflow.com/questions/13003644/async-loading-javascript-with-document-write) – Michał Perłakowski Dec 26 '15 at 14:30

16 Answers16

317
var my_awesome_script = document.createElement('script');

my_awesome_script.setAttribute('src','http://example.com/site.js');

document.head.appendChild(my_awesome_script);
danp
  • 14,876
  • 6
  • 42
  • 48
  • 3
    I wonder why there is no `src` property in js, like `script.src='path_to_js'`. Wait, it is there.. – Timo Jul 08 '22 at 16:23
130

You can use the document.createElement() function like this:

function addScript( src ) {
  var s = document.createElement( 'script' );
  s.setAttribute( 'src', src );
  document.body.appendChild( s );
}
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
Sirko
  • 72,589
  • 19
  • 149
  • 183
96

There is the onload function, that could be called when the script has loaded successfully:

function addScript( src, callback ) {
  var s = document.createElement( 'script' );
  s.setAttribute( 'src', src );
  s.onload=callback;
  document.body.appendChild( s );
}
simhumileco
  • 31,877
  • 16
  • 137
  • 115
MSS
  • 3,520
  • 24
  • 29
31

It's almost a decade later and nobody bothers to write the Promise version, so here is mine (based on this awnser):

function addScript(src) {
  return new Promise((resolve, reject) => {
    const s = document.createElement('script');

    s.setAttribute('src', src);
    s.addEventListener('load', resolve);
    s.addEventListener('error', reject);

    document.body.appendChild(s);
  });
}

Usage

try {
  await addScript('https://api.stackexchange.com/js/2.0/all.js');
  // do something after it was loaded
} catch (e) {
  console.log(e);
}
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
21

a nice little script I wrote to load multiple scripts:

function scriptLoader(scripts, callback) {

    var count = scripts.length;

    function urlCallback(url) {
        return function () {
            console.log(url + ' was loaded (' + --count + ' more scripts remaining).');
            if (count < 1) {
                callback();
            }
        };
    }

    function loadScript(url) {
        var s = document.createElement('script');
        s.setAttribute('src', url);
        s.onload = urlCallback(url);
        document.head.appendChild(s);
    }

    for (var script of scripts) {
        loadScript(script);
    }
};

usage:

scriptLoader(['a.js','b.js'], function() {
    // use code from a.js or b.js
});
EliSherer
  • 1,597
  • 1
  • 17
  • 29
17

When scripts are loaded asynchronously they cannot call document.write. The calls will simply be ignored and a warning will be written to the console.

You can use the following code to load the script dynamically:

var scriptElm = document.createElement('script');
scriptElm.src = 'source.js';
document.body.appendChild(scriptElm);

This approach works well only when your source belongs to a separate file.

But if you have source code as inline functions which you want to load dynamically and want to add other attributes to the script tag, e.g. class, type, etc., then the following snippet would help you:

var scriptElm = document.createElement('script');
scriptElm.setAttribute('class', 'class-name');
var inlineCode = document.createTextNode('alert("hello world")');
scriptElm.appendChild(inlineCode); 
document.body.appendChild(scriptElm);
Malay
  • 635
  • 8
  • 11
12

You can try following code snippet.

function addScript(attribute, text, callback) {
    var s = document.createElement('script');
    for (var attr in attribute) {
        s.setAttribute(attr, attribute[attr] ? attribute[attr] : null)
    }
    s.innerHTML = text;
    s.onload = callback;
    document.body.appendChild(s);
}

addScript({
    src: 'https://www.google.com',
    type: 'text/javascript',
    async: null
}, '<div>innerHTML</div>', function(){});
vaenow
  • 164
  • 1
  • 9
9

A one-liner (no essential difference to the answers above though):

document.body.appendChild(document.createElement('script')).src = 'source.js';
JohnW
  • 614
  • 6
  • 6
6

This Is Work For Me.

You Can Check It.

var script_tag = document.createElement('script');
script_tag.setAttribute('src','https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js');
document.head.appendChild(script_tag);
window.onload = function() {
    if (window.jQuery) {  
        // jQuery is loaded  
        alert("ADD SCRIPT TAG ON HEAD!");
    } else {
        // jQuery is not loaded
        alert("DOESN'T ADD SCRIPT TAG ON HEAD");
    }
}
JD Savaj
  • 803
  • 8
  • 16
4

Loads scripts that depends on one another with the right order.

Based on Satyam Pathak response, but fixed the onload. It was triggered before the script actually loaded.

const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
let count = 0

  
 const recursivelyAddScript = (script, cb) => {
  const el = document.createElement('script')
  el.src = script
  if(count < scripts.length) {
    count ++
    el.onload = () => recursivelyAddScript(scripts[count])
    document.body.appendChild(el)
  } else {
    console.log('All script loaded')
    return
  }
}
 
  recursivelyAddScript(scripts[count])
kolorafa
  • 74
  • 3
3

Well, there are multiple ways you can include dynamic javascript, I use this one for many of the projects.

var script = document.createElement("script")
script.type = "text/javascript";
//Chrome,Firefox, Opera, Safari 3+
script.onload = function(){
console.log("Script is loaded");
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);

You can call create a universal function which can help you to load as many javascript files as needed. There is a full tutorial about this here.

Inserting Dynamic Javascript the right way

2

No one mentioned it, but you can also stick the actual source code into a script tag by making a URL out of it using URL and Blob:

const jsCode = `
 // JS code in here. Maybe you extracted it from some HTML string.
`

const url = URL.createObjectURL(new Blob([jsCode]))
const script = document.createElement('script')
script.src = url
URL.revokeObjectURL(url) // dispose of it when done

as for the jsCode, you may have gotten it from some HTML.

Here's a more full example of how you'd handle any number of scripts in an HTML source:

main()

async function main() {
    const scriptTagOpen = /<script\b[^>]*>/g
    const scriptTagClose = /<\/script\b[^>]*>/g
    const scriptTagRegex = /<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>/g

    const response = await fetch('path/to/some.html')
    const html = await response.text()

    someElement.innerHTML = html

    // We need to get the script tags and manually add them to DOM
    // because otherwise innerHTML will not execute them.
    const codes =
        html
            .match(scriptTagRegex)
            ?.map(code => code.replace(scriptTagOpen, '').replace(scriptTagClose, ''))
            .map(code => URL.createObjectURL(new Blob([code]))) || []

    for (const code of codes) {
        const script = document.createElement('script')
        script.src = code
        someElement.append(script)
        URL.revokeObjectURL(code)
    }
}
trusktr
  • 44,284
  • 53
  • 191
  • 263
1

the only way to do this is to replace document.write with your own function which will append elements to the bottom of your page. It is pretty straight forward with jQuery:

document.write = function(htmlToWrite) {
  $(htmlToWrite).appendTo('body');
}

If you have html coming to document.write in chunks like the question example you'll need to buffer the htmlToWrite segments. Maybe something like this:

document.write = (function() {
  var buffer = "";
  var timer;
  return function(htmlPieceToWrite) {
    buffer += htmlPieceToWrite;
    clearTimeout(timer);
    timer = setTimeout(function() {
      $(buffer).appendTo('body');
      buffer = "";
    }, 0)
  }
})()
Scott Jungwirth
  • 6,105
  • 3
  • 38
  • 35
1

I tried it by recursively appending each script

Note If your scripts are dependent one after other, then position will need to be in sync.

Major Dependency should be in last in array so that initial scripts can use it

const scripts = ['https://www.gstatic.com/firebasejs/6.2.0/firebase-storage.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-firestore.js', 'https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js']
let count = 0

  
 const recursivelyAddScript = (script, cb) => {
  const el = document.createElement('script')
  el.src = script
  if(count < scripts.length) {
    count ++
    el.onload = recursivelyAddScript(scripts[count])
    document.body.appendChild(el)
  } else {
    console.log('All script loaded')
    return
  }
}
 
  recursivelyAddScript(scripts[count])
Satyam Pathak
  • 6,612
  • 3
  • 25
  • 52
0

Here is a minified snippet, same code as Google Analytics and Facebook Pixel uses:

!function(e,s,t){(t=e.createElement(s)).async=!0,t.src="https://example.com/foo.js",(e=e.getElementsByTagName(s)[0]).parentNode.insertBefore(t,e)}(document,"script");

Replace https://example.com/foo.js with your script path.

Moshe Simantov
  • 3,937
  • 2
  • 25
  • 35
-1

window.addEventListener("load", init);

        const loadScript = async (url) => {
            const response = await fetch(url);
            const script = await response.text();
            eval(script);
        }

        function init() {
            const wistiaVideo = document.querySelector(".wistia_embed");

            if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype) {
                let lazyVideoObserver = new IntersectionObserver(function (entries, observer) {
                    entries.forEach(function (entry) {
                        if (entry.isIntersecting) {
                            setTimeout(() => loadScript("//fast.wistia.com/assets/external/E-v1.js"), 1000);
                            lazyVideoObserver.unobserve(entry.target);
                            console.log("E-v1.js script loaded from fast.wistia.com");
                        }
                    });
                });
                lazyVideoObserver.observe(wistiaVideo);
            }
        }
<div style="height: 150vh; background-color: #f7f7f7;"></div>
    <h1>Wistia Video!</h1>

    <div class="wistia_embed wistia_async_29b0fbf547" style="width:640px;height:360px;">&nbsp;</div>


    <h1>Video Ended!</h1>
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-ask). – Community Sep 21 '21 at 06:33