94

I'm creating a jquery plugin and I want to verify an external script is loaded. This is for an internal web app and I can keep the script name/location consistent(mysscript.js). This is also an ajaxy plugin that can be called on many times on the page.

If I can verify the script is not loaded I'll load it using:

jQuery.getScript()

How can I verify the script is loaded because I don't want the same script loaded on the page more than once? Is this something that I shouldn't need to worry about due to caching of the script?

Update: I may not have control over who uses this plugin in our organization and may not be able to enforce that the script is not already on the page with or without a specific ID, but the script name will always be in the same place with the same name. I'm hoping I can use the name of the script to verify it's actually loaded.

AGoodDisplayName
  • 5,543
  • 7
  • 35
  • 50
  • This thread is in top of the SO jquery tags FAQ and has a huge variety of methods to check for exisitng script https://stackoverflow.com/questions/1014203/best-way-to-use-googles-hosted-jquery-but-fall-back-to-my-hosted-library-on-go Lots of feedback on various methods – charlietfl Mar 01 '12 at 17:57

12 Answers12

141

If the script creates any variables or functions in the global space you can check for their existance:

External JS (in global scope) --

var myCustomFlag = true;

And to check if this has run:

if (typeof window.myCustomFlag == 'undefined') {
    //the flag was not found, so the code has not run
    $.getScript('<external JS>');
}

Update

You can check for the existence of the <script> tag in question by selecting all of the <script> elements and checking their src attributes:

//get the number of `<script>` elements that have the correct `src` attribute
var len = $('script').filter(function () {
    return ($(this).attr('src') == '<external JS>');
}).length;

//if there are no scripts that match, the load it
if (len === 0) {
    $.getScript('<external JS>');
}

Or you can just bake this .filter() functionality right into the selector:

var len = $('script[src="<external JS>"]').length;
Jasper
  • 75,717
  • 14
  • 151
  • 146
  • This was a good answer, unfortunately the jquery.getScript method adds the script with no source specified. So I was not able to find it. I did use part of your answer to get this solved so +1. – AGoodDisplayName Mar 01 '12 at 19:03
  • @AGoodDisplayName In the callback for `$.getScript()` you can set a global flag that can be checked: `$.getScript(', function () { window.myCustomFlag = true; }')`. Then you can just check for the existence of the specific ` – Jasper Mar 01 '12 at 19:04
  • @jasper, yeah that's probably the most efficient way to do it which is why I am accepting your answer. Just got done putting my solution in (in which I should include a global flag). – AGoodDisplayName Mar 01 '12 at 19:20
  • 39
    Script src checking is ok, but the problem is that existence of script tag with correct src doesn't imply that the very script is loaded. Inserting tag is only the beginning of the loading phase. – Karol Aug 11 '14 at 00:42
  • @Carlos It's been a while since I looked at this answer but if you run this code last, any other script above it should already have been loaded and executed. That's true even if this code is inline, it'll have to wait for all JS to have executed that comes before it, even if that JS is externally hosted. – Jasper Aug 12 '14 at 19:00
  • 3
    @Jasper from a question I assumed that scripts are loaded dynamically, by inserting the tag into DOM. This is the only case when you can have the same script inserted multiple times (nobody inserts the script in HTML, and then tries to load it again dynamically). So once you insert script tag, you still wait till it's loaded, and in this case your answer will not work, because it only checks whether script tag exists or not. – Karol Aug 12 '14 at 22:58
  • As @Karol stated, this solution doesn't tell you that the script has actually loaded. See my answer for the correct approach. – lonix Nov 13 '22 at 10:35
58

Few too many answers on this one, but I feel it's worth adding this solution. It combines a few different answers.

Key points for me were

  • add an #id tag, so it's easy to find, and not duplicate
  • Use .onload() to wait until the script has finished loading before using it

    mounted() {
      // First check if the script already exists on the dom
      // by searching for an id
      let id = 'googleMaps'
      if(document.getElementById(id) === null) {
        let script = document.createElement('script')
        script.setAttribute('src', 'https://maps.googleapis.com/maps/api/js?key=' + apiKey)
        script.setAttribute('id', id)
        document.body.appendChild(script) 
    
        // now wait for it to load...
        script.onload = () => {
            // script has loaded, you can now use it safely
            alert('thank me later')
            // ... do something with the newly loaded script
        }      
      }
    }
    
atlas_scoffed
  • 3,542
  • 30
  • 46
  • 7
    `script.onload` is the correct answer here. Thanks for sharing! – Vigs Dec 08 '20 at 01:06
  • 3
    Answers like these are why I don't trust the accepted response anymore... thanks! `script.onload` actually waits to take action until the loading is finished, which is exactly what I needed. – jetsetter Feb 28 '21 at 16:57
  • 1
    Yep, it's annoying, but often worth the effort of checking through all the answers. Things change, and often different answers can each provide something useful. – atlas_scoffed Mar 01 '21 at 20:52
35

@jasper's answer is totally correct but with modern browsers, a standard Javascript solution could be:

function isScriptLoaded(src)
{
    return Boolean(document.querySelector('script[src="' + src + '"]'));
}

UPDATE July 2021:

The accepted solutions above have changed & improved much over time. The scope of my previous answer above was only to detect if the script was inserted in the document to load (and not whether the script has actually finished loading).

To detect if the script has already loaded, I use the following method (in general):

  1. Create a common library function to dynamically load all scripts.
  2. Before loading, it uses the isScriptLoaded(src) function above to check whether the script has already been added (say, by another module).
  3. I use something like the following loadScript() function to load the script that uses callback functions to inform the calling modules if the script finished loading successfully.
  4. I also use additional logic to retry when script loading fails (in case of temporary network issues).
    1. Retry is done by removing the <script> tag from the body and adding it again.
    2. If it still fails to load after configured number of retries, the <script> tag is removed from the body.
    3. I have removed that logic from the following code for simplicity. It should be easy to add.

/** 
 * Mark/store the script as fully loaded in a global variable.
 * @param src URL of the script
 */
function markScriptFullyLoaded(src) {
    window.scriptLoadMap[src] = true;
}

/** 
 * Returns true if the script has been added to the page
 * @param src URL of the script
 */
function isScriptAdded(src) {
    return Boolean(document.querySelector('script[src="' + src + '"]'));
}

/** 
 * Returns true if the script has been fully loaded
 * @param src URL of the script
 */
function isScriptFullyLoaded(src) {
    return src in window.scriptLoadMap && window.scriptLoadMap[src];
}

/** 
 * Load a script. 
 * @param src URL of the script
 * @param onLoadCallback Callback function when the script is fully loaded
 * @param onLoadErrorCallback Callback function when the script fails to load
 * @param retryCount How many times retry laoding the script? (Not implimented here. Logic goes into js.onerror function)
 */
function loadScript(src, onLoadCallback, onLoadErrorCallback, retryCount) {
    if (!src) return;
    
    // Check if the script is already loaded
    if ( isScriptAdded(src) )
    {
        // If script already loaded successfully, trigger the callback function
        if (isScriptFullyLoaded(src)) onLoadCallback();
        
        console.warn("Script already loaded. Skipping: ", src);
        return;
    }

    // Loading the script...
    const js = document.createElement('script');
    js.setAttribute("async", "");
    js.src = src;
    
    js.onload = () => {
        markScriptFullyLoaded(src)
        
        // Optional callback on script load
        if (onLoadCallback) onLoadCallback();
    };
    
    js.onerror = () => {
        // Remove the script node (to be able to try again later)
        const js2 = document.querySelector('script[src="' + src +'"]');
        js2.parentNode.removeChild(js2);
        
        // Optional callback on script load failure
        if (onLoadErrorCallback) onLoadErrorCallback();
    };

    document.head.appendChild(js);
}

Abhishek
  • 3,439
  • 2
  • 20
  • 10
11

This was very simple now that I realize how to do it, thanks to all the answers for leading me to the solution. I had to abandon $.getScript() in order to specify the source of the script...sometimes doing things manually is best.

Solution

//great suggestion @Jasper
var len = $('script[src*="Javascript/MyScript.js"]').length; 

if (len === 0) {
        alert('script not loaded');

        loadScript('Javascript/MyScript.js');

        if ($('script[src*="Javascript/MyScript.js"]').length === 0) {
            alert('still not loaded');
        }
        else {
            alert('loaded now');
        }
    }
    else {
        alert('script loaded');
    }


function loadScript(scriptLocationAndName) {
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = scriptLocationAndName;
    head.appendChild(script);
}
AGoodDisplayName
  • 5,543
  • 7
  • 35
  • 50
  • This tells you that the tag was inserted into the DOM, not that the script was actually loaded. See my answer for the correct approach. – lonix Nov 13 '22 at 10:37
9

Create the script tag with a specific ID and then check if that ID exists?

Alternatively, loop through script tags checking for the script 'src' and make sure those are not already loaded with the same value as the one you want to avoid ?

Edit: following feedback that a code example would be useful:

(function(){
    var desiredSource = 'https://sitename.com/js/script.js';
    var scripts       = document.getElementsByTagName('script');
    var alreadyLoaded = false;

    if(scripts.length){
        for(var scriptIndex in scripts) {
            if(!alreadyLoaded && desiredSource === scripts[scriptIndex].src) {
                alreadyLoaded = true;
            }
        }
    }
    if(!alreadyLoaded){
        // Run your code in this block?
    }
})();

As mentioned in the comments (https://stackoverflow.com/users/1358777/alwin-kesler), this may be an alternative (not benchmarked):

(function(){
    var desiredSource = 'https://sitename.com/js/script.js';
    var scripts = document.getElementsByTagName('script');
    var alreadyLoaded = false;

    for(var scriptIndex in document.scripts) {
        if(!alreadyLoaded && desiredSource === scripts[scriptIndex].src) {
            alreadyLoaded = true;
        }
    }
    if(!alreadyLoaded){
        // Run your code in this block?
    }
})();
DutchPrince
  • 333
  • 2
  • 16
MyStream
  • 2,533
  • 1
  • 16
  • 33
5

Simply check if the global variable is available, if not check again. In order to prevent the maximum callstack being exceeded set a 100ms timeout on the check:

function check_script_loaded(glob_var) {
    if(typeof(glob_var) !== 'undefined') {
    // do your thing
    } else {
    setTimeout(function() {
    check_script_loaded(glob_var)
    }, 100)
    }
}
Cybernetic
  • 12,628
  • 16
  • 93
  • 132
4

Another way to check an external script is loaded or not, you can use data function of jquery and store a validation flag. Example as :

if(!$("body").data("google-map"))
    {
        console.log("no js");

        $.getScript("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=initilize",function(){
            $("body").data("google-map",true);

            },function(){
                alert("error while loading script");
            });
        }
    }
    else
    {
        console.log("js already loaded");
    }
Sunil Sharma
  • 1,297
  • 3
  • 17
  • 31
4

I think it's better to use window.addEventListener('error') to capture the script load error and try to load it again. It's useful when we load scripts from a CDN server. If we can't load script from the CDN, we can load it from our server.

window.addEventListener('error', function(e) {
  if (e.target.nodeName === 'SCRIPT') {
    var scriptTag = document.createElement('script');
    scriptTag.src = e.target.src.replace('https://static.cdn.com/', '/our-server/static/');
    document.head.appendChild(scriptTag);
  }
}, true);
Duke
  • 219
  • 1
  • 4
  • 11
3

Merging several answers from above into an easy to use function

function GetScriptIfNotLoaded(scriptLocationAndName)
{
  var len = $('script[src*="' + scriptLocationAndName +'"]').length;

  //script already loaded!
  if (len > 0)
      return;

  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = scriptLocationAndName;
  head.appendChild(script);
}
Aaron Sherman
  • 3,789
  • 1
  • 30
  • 33
2

Some answers on this page are wrong. They check for the existence of the <script> tag - but that is not enough. That tells you that the tag was inserted into the DOM, not that the script is finished loading.

I assume from the question that there are two parts: the code that inserts the script, and the code that checks whether the script has loaded.

The code that dynamically inserts the script:

let tag    = document.createElement('script');
tag.type   = 'text/javascript';
tag.id     = 'foo';
tag.src    = 'https://cdn.example.com/foo.min.js';
tag.onload = () => tag.setAttribute('data-loaded', true);   // magic sauce
document.body.appendChild(tag);

Some other code, that checks whether the script has loaded:

let script   = document.getElementById('foo');
let isLoaded = script && script.getAttribute('data-loaded') === 'true';
console.log(isLoaded);     // true

If the both of those things (inserting and checking) are in the same code block, then you could simplify the above:

tag.onload = () => console.log('loaded');
lonix
  • 14,255
  • 23
  • 85
  • 176
1

My idead is to listen the error log if there is an error on script loading.

const checkSegmentBlocked = (e) => {
  if (e.target.nodeName === 'SCRIPT' && e.target.src.includes('analytics.min.js')) {
      window.isSegmentBlocked = true;
      e.target.removeEventListener(e.type, checkSegmentBlocked);
  }
};
window.addEventListener('error', checkSegmentBlocked, true);
    
Nate Z
  • 41
  • 4
0

I found a quick tip before you start diving into code that might save a bit of time. Check devtools on the webpage and click on the network tab. The js scripts are shown if they are loaded as a 200 response from the server.

Spinstaz
  • 287
  • 6
  • 12