验证外部脚本已加载

IT技术 javascript jquery asp.net
2021-01-30 19:32:45

我正在创建一个 jquery 插件,我想验证一个外部脚本是否已加载。这是一个内部网络应用程序,我可以保持脚本名称/位置一致(mysscript.js)。这也是一个ajaxy插件,可以在页面上多次调用。

如果我可以验证脚本未加载,我将使用以下方法加载它:

jQuery.getScript()

我如何验证脚本是否已加载,因为我不想在页面上多次加载相同的脚本?由于脚本缓存,这是我不需要担心的事情吗?

更新: 我可能无法控制谁在我们的组织中使用这个插件,并且可能无法强制脚本不在页面上,无论是否有特定的 ID,但脚本名称将始终与同名。我希望我可以使用脚本的名称来验证它是否已实际加载。

6个回答

如果脚本在全局空间中创建了任何变量或函数,您可以检查它们是否存在:

外部 JS(在全局范围内)--

var myCustomFlag = true;

并检查这是否已运行:

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

更新

您可以<script>通过选择所有<script>元素并检查它们的src属性来检查相关标签是否存在

//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>');
}

或者您可以直接将此.filter()功能烘焙到选择器中:

var len = $('script[src="<external JS>"]').length;
脚本 src 检查没问题,但问题是具有正确 src 的脚本标记的存在并不意味着该脚本已加载。插入标签只是加载阶段的开始。
2021-03-18 19:32:45
@jasper,是的,这可能是最有效的方法,这就是我接受您的答案的原因。刚刚完成了我的解决方案(我应该在其中包含一个全局标志)。
2021-03-19 19:32:45
@Jasper 来自一个问题,我假设脚本是通过将标签插入 DOM 来动态加载的。这是您可以多次插入相同脚本的唯一情况(没有人在 HTML 中插入脚本,然后尝试再次动态加载它)。所以一旦你插入了 script 标签,你仍然要等到它被加载,在这种情况下你的答案将不起作用,因为它只检查 script 标签是否存在。
2021-03-29 19:32:45
这是一个很好的答案,不幸的是 jquery.getScript 方法添加了没有指定源的脚本。所以我无法找到它。我确实使用了你的部分答案来解决这个问题,所以 +1。
2021-03-31 19:32:45
@AGoodDisplayName 在回调中$.getScript()可以设置一个全局标志,可以检查:$.getScript('<external JS>, function () { window.myCustomFlag = true; }')然后您可以检查特定<script>元素或myCustomFlag变量是否存在
2021-04-08 19:32:45

关于这个的答案很少,但我觉得值得添加这个解决方案。它结合了几个不同的答案。

对我来说关键点是

  • 添加#id标签,方便查找,不重复
  • 使用 .onload() 等待脚本加载完成后再使用

    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
        }      
      }
    }
    
是的,这很烦人,但通常值得检查所有答案。事情会发生变化,通常不同的答案都可以提供有用的信息。
2021-03-24 19:32:45
像这样的答案就是为什么我不再相信已接受的回复......谢谢! script.onload实际上等到加载完成后才采取行动,这正是我所需要的。
2021-03-27 19:32:45
script.onload是这里的正确答案。感谢分享!
2021-04-04 19:32:45

@jasper 的回答是完全正确的,但是对于现代浏览器,标准的 Javascript 解决方案可能是:

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

2021 年 7 月更新:

随着时间的推移,上述公认的解决方案已经发生了很大的变化和改进。我之前回答的范围只是检测脚本是否插入到要加载的文档中(而不是脚本是否实际完成加载)。

为了检测脚本是否已经加载,我使用以下方法(通常):

  1. 创建一个公共库函数来动态加载所有脚本。
  2. 在加载之前,它使用isScriptLoaded(src)上面函数来检查脚本是否已经被添加(比如,被另一个module添加)。
  3. 我使用类似下面的loadScript()函数来加载脚本,如果脚本成功加载完成,该脚本使用回调函数通知调用module。
  4. 我还使用额外的逻辑在脚本加载失败时重试(在临时网络问题的情况下)。
    1. 重试是通过<script>从正文中删除标签并再次添加来完成的。
    2. 如果在配置的重试次数后仍然无法加载,则<script>标记将从正文中删除。
    3. 为简单起见,我已从以下代码中删除了该逻辑。应该很容易添加。

/** 
 * 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);
}

Boolean如果您愿意,您可以使用而不是条件运算符
2021-03-21 19:32:45
它可以只是 return document.querySelector('script[src="' + src + '"]');
2021-03-21 19:32:45
这对我不起作用。例如:document.querySelector('head').append(script_el); console.log(isScriptLoaded(script_el.src)返回true但脚本尚未运行。
2021-03-27 19:32:45

这很简单,现在我知道如何去做了,感谢所有引导我找到解决方案的答案。我不得不放弃 $.getScript() 以指定脚本的来源......有时手动做事是最好的。

解决方案

//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);
}

创建具有特定 ID 的脚本标记,然后检查该 ID 是否存在?

或者,循环检查脚本“src”的脚本标签,并确保这些标签尚未加载与您想要避免的相同的值?

编辑:遵循代码示例有用的反馈:

(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?
    }
})();

正如评论中提到的(https://stackoverflow.com/users/1358777/alwin-kesler),这可能是另一种选择(未进行基准测试):

(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?
    }
})();
这是完美的解决方案。我推荐第二个。
2021-04-10 19:32:45
如果可以将这些想法放在代码中,那就太好了。
2021-04-11 19:32:45
document.scripts 似乎比document.getElementsByTagName('script')但还没有测试它更快
2021-04-11 19:32:45
@AlwinKesler,同意。使用替代方法更新了代码。得到一个基准会很好。
2021-04-12 19:32:45