如何判断<script>标记是否加载失败

Dav*_*vid 103 javascript script-tag onerror

我正在动态地向<script>页面添加标签<head>,我希望能够以某种方式判断加载是否失败 - 404,加载脚本中的脚本错误等等.

在Firefox中,这有效:

var script_tag = document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', 'http://fail.org/nonexistant.js');
script_tag.onerror = function() { alert("Loading failed!"); }
document.getElementsByTagName('head')[0].appendChild(script_tag);
Run Code Online (Sandbox Code Playgroud)

但是,这在IE或Safari中不起作用.

有没有人知道如何在Firefox以外的浏览器中使用它?

(我不认为需要在.js文件中放置特殊代码的解决方案是一个很好的解决方案.它不够优雅且不灵活.)

Dio*_*ane 31

脚本标记没有错误事件.您可以判断它何时成功,并假设它在超时后未加载:

<script type="text/javascript" onload="loaded=1" src="....js"></script>
Run Code Online (Sandbox Code Playgroud)

  • 好吧,是的,如果文件LOADS并且文件本身有错误,但如果文件没有提供,则onload将永远不会触发. (37认同)
  • 即使出现javascript错误,也会触发"onload"侦听器. (7认同)
  • 脚本标记有错误.未找到资源时将触发. (3认同)
  • @Diodeus-JamesMacFarlane 正如其他人评论的那样,至少在所有现代浏览器中,*存在*一个“错误”事件 - 因错误地声明“脚本标记没有错误事件”而投了反对票。请更新或删除这个过时/错误的答案。 (2认同)

and*_*rom 24

如果您只关心html5浏览器,则可以使用错误事件(因为这仅用于错误处理,因此只能在KISS IMHO的下一代浏览器上支持此功能).

从规格:

如果src属性的值为空字符串或者无法解析,则用户代理必须将任务排队以在元素上触发名为error的简单事件,并中止这些步骤.

如果加载导致错误(例如DNS错误或HTTP 404错误)执行脚本块必须只包括在元素处触发名为error的简单事件.

这意味着您不必进行任何容易出错的轮询,并且可以将其与async和defer属性结合使用,以确保脚本不会阻止页面呈现:

即使指定了async属性,也可以指定defer属性,以使仅支持延迟(而非异步)的旧Web浏览器回退到延迟行为而不是默认的同步阻塞行为.

有关http://www.w3.org/TR/html5/scripting-1.html#script的更多信息

  • `<script src ="nonexistent.js"onerror ="alert('error!')"> </ script>`[小提琴](https://jsfiddle.net/dtc16732/) (12认同)

Ara*_*yan 20

来自Erwinus的剧本效果很好,但编码不是很清楚.我冒昧地把它清理干净并破译它正在做的事情.我做了这些改变:

  • 有意义的变量名称
  • 使用prototype.
  • require() 使用参数变量
  • alert()默认情况下不返回任何消息
  • 修复了我遇到的一些语法错误和范围问题

再次感谢Erwinus,功能本身就是现货.

function ScriptLoader() {
}

ScriptLoader.prototype = {

    timer: function (times, // number of times to try
                     delay, // delay per try
                     delayMore, // extra delay per try (additional to delay)
                     test, // called each try, timer stops if this returns true
                     failure, // called on failure
                     result // used internally, shouldn't be passed
            ) {
        var me = this;
        if (times == -1 || times > 0) {
            setTimeout(function () {
                result = (test()) ? 1 : 0;
                me.timer((result) ? 0 : (times > 0) ? --times : times, delay + ((delayMore) ? delayMore : 0), delayMore, test, failure, result);
            }, (result || delay < 0) ? 0.1 : delay);
        } else if (typeof failure == 'function') {
            setTimeout(failure, 1);
        }
    },

    addEvent: function (el, eventName, eventFunc) {
        if (typeof el != 'object') {
            return false;
        }

        if (el.addEventListener) {
            el.addEventListener(eventName, eventFunc, false);
            return true;
        }

        if (el.attachEvent) {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    },

    // add script to dom
    require: function (url, args) {
        var me = this;
        args = args || {};

        var scriptTag = document.createElement('script');
        var headTag = document.getElementsByTagName('head')[0];
        if (!headTag) {
            return false;
        }

        setTimeout(function () {
            var f = (typeof args.success == 'function') ? args.success : function () {
            };
            args.failure = (typeof args.failure == 'function') ? args.failure : function () {
            };
            var fail = function () {
                if (!scriptTag.__es) {
                    scriptTag.__es = true;
                    scriptTag.id = 'failed';
                    args.failure(scriptTag);
                }
            };
            scriptTag.onload = function () {
                scriptTag.id = 'loaded';
                f(scriptTag);
            };
            scriptTag.type = 'text/javascript';
            scriptTag.async = (typeof args.async == 'boolean') ? args.async : false;
            scriptTag.charset = 'utf-8';
            me.__es = false;
            me.addEvent(scriptTag, 'error', fail); // when supported
            // when error event is not supported fall back to timer
            me.timer(15, 1000, 0, function () {
                return (scriptTag.id == 'loaded');
            }, function () {
                if (scriptTag.id != 'loaded') {
                    fail();
                }
            });
            scriptTag.src = url;
            setTimeout(function () {
                try {
                    headTag.appendChild(scriptTag);
                } catch (e) {
                    fail();
                }
            }, 1);
        }, (typeof args.delay == 'number') ? args.delay : 1);
        return true;
    }
};

$(document).ready(function () {
    var loader = new ScriptLoader();
    loader.require('resources/templates.js', {
        async: true, success: function () {
            alert('loaded');
        }, failure: function () {
            alert('NOT loaded');
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

  • 我被迫使用jQuery的[$ .getScript](http://api.jquery.com/jQuery.getScript/),因为这个函数对MSIE8中的缓存脚本失败了,不幸的是 (4认同)
  • 是的,Aram,肯定会做的伎俩,但它也会使浏览器的缓存无效,所以这种情况下的开销可能不值得;) (2认同)

Cod*_*eat 16

我知道这是一个老线程,但我得到了一个很好的解决方案(我认为).它是从我的一个类中复制的,它处理所有AJAX的东西.

当无法加载脚本时,它会设置一个错误处理程序,但是当不支持错误处理程序时,它会回退到一个检查错误15秒的计时器.

function jsLoader()
{
    var o = this;

    // simple unstopable repeat timer, when t=-1 means endless, when function f() returns true it can be stopped
    o.timer = function(t, i, d, f, fend, b)
    {
        if( t == -1 || t > 0 )
        {
            setTimeout(function() {
                b=(f()) ? 1 : 0;
                o.timer((b) ? 0 : (t>0) ? --t : t, i+((d) ? d : 0), d, f, fend,b );
            }, (b || i < 0) ? 0.1 : i);
        }
        else if(typeof fend == 'function')
        {
            setTimeout(fend, 1);
        }
    };

    o.addEvent = function(el, eventName, eventFunc)
    {
        if(typeof el != 'object')
        {
            return false;
        }

        if(el.addEventListener)
        {
            el.addEventListener (eventName, eventFunc, false);
            return true;
        }

        if(el.attachEvent)
        {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    };

    // add script to dom
    o.require = function(s, delay, baSync, fCallback, fErr)
    {
        var oo = document.createElement('script'),
        oHead = document.getElementsByTagName('head')[0];
        if(!oHead)
        {
            return false;
        }

        setTimeout( function() {
            var f = (typeof fCallback == 'function') ? fCallback : function(){};
            fErr = (typeof fErr == 'function') ? fErr : function(){
                alert('require: Cannot load resource -'+s);
            },
            fe = function(){
                if(!oo.__es)
                {
                    oo.__es = true;
                    oo.id = 'failed';
                    fErr(oo);
                }
            };
            oo.onload = function() {
                oo.id = 'loaded';
                f(oo);
            };
            oo.type = 'text/javascript';
            oo.async = (typeof baSync == 'boolean') ? baSync : false;
            oo.charset = 'utf-8';
            o.__es = false;
            o.addEvent( oo, 'error', fe ); // when supported

            // when error event is not supported fall back to timer
            o.timer(15, 1000, 0, function() {
                return (oo.id == 'loaded');
            }, function(){ 
                if(oo.id != 'loaded'){
                    fe();
                }
            });
            oo.src = s;
            setTimeout(function() {
                try{
                    oHead.appendChild(oo);
                }catch(e){
                    fe();
                }
            },1); 
        }, (typeof delay == 'number') ? delay : 1);  
        return true;
    };

}

$(document).ready( function()
{
    var ol = new jsLoader();
    ol.require('myscript.js', 800, true, function(){
        alert('loaded');
    }, function() {
        alert('NOT loaded');
    });
});
Run Code Online (Sandbox Code Playgroud)

  • 无论这是否有效,阅读都很痛苦 - 更不用说调试了.这是一些相当糟糕的格式和真正残酷的编码风格.变量命名本身就是一团糟. (18认同)
  • 如果您没有看到可读代码的价值(并且您可以在不对实际执行的代码进行任何更改的情况下使其可读),那么我会为每个必须与您和您的代码合作的人感到遗憾. (7认同)
  • ... jQuery来自哪里? (4认同)
  • 这个剧本对我很有帮助,感谢Erwinus. (3认同)

Luc*_*eis 9

要检查是否在JavaScript nonexistant.js返回任何错误,你必须里面添加变量http://fail.org/nonexistant.js一样var isExecuted = true;,然后检查是否加载脚本标签时,它的存在.

但是,如果你只想检查nonexistant.js没有404返回(意味着它存在),你可以尝试使用isLoaded变量...

var isExecuted = false;
var isLoaded = false;
script_tag.onload = script_tag.onreadystatechange = function() {
    if(!this.readyState ||
        this.readyState == "loaded" || this.readyState == "complete") {
        // script successfully loaded
        isLoaded = true;

        if(isExecuted) // no error
    }
}
Run Code Online (Sandbox Code Playgroud)

这将涵盖两种情况.


per*_*mon 9

我的工作清洁解决方案(2017)

function loaderScript(scriptUrl){
   return new Promise(function (res, rej) {
    let script = document.createElement('script');
    script.src = scriptUrl;
    script.type = 'text/javascript';
    script.onError = rej;
    script.async = true;
    script.onload = res;
    script.addEventListener('error',rej);
    script.addEventListener('load',res);
    document.head.appendChild(script);
 })

}
Run Code Online (Sandbox Code Playgroud)

正如Martin所指出的那样:

function loaderScript(scriptUrl){
   return new Promise(function (res, rej) {
    let script = document.createElement('script');
    script.src = scriptUrl;
    script.type = 'text/javascript';
    script.onError = rej;
    script.async = true;
    script.onload = res;
    script.addEventListener('error',rej);
    script.addEventListener('load',res);
    document.head.appendChild(script);
 })

}
Run Code Online (Sandbox Code Playgroud)


Gre*_*hak 6

我希望这不会被低估,因为在特殊情况下,这是解决问题的最可靠方法.只要服务器允许您使用CORS(http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)获取Javascript资源,您就可以获得丰富的选项.

使用XMLHttpRequest获取资源将适用于所有现代浏览器,包括IE.由于您希望加载Javascript,因此您首先可以使用Javascript.您可以使用readyState跟踪进度(http://en.wikipedia.org/wiki/XMLHttpRequest#The_onreadystatechange_event_listener).最后,一旦收到文件的内容,就可以使用eval()执行它.是的,我说eval - 因为安全方面它与正常加载脚本没什么不同.实际上,John Resig建议使用类似的技术来获得更好的标签(http://ejohn.org/blog/degrading-script-tags/).

此方法还允许您将加载与eval分开,并在eval发生之前和之后执行函数.当并行加载脚本但逐个评估它们时,它变得非常有用 - 当您将标记放在HTML中时,浏览器可以轻松完成这些操作,但是不要让您在运行时使用Javascript添加脚本.

对于加载脚本,CORS也优于JSONP(http://en.wikipedia.org/wiki/XMLHttpRequest#Cross-domain_requests).但是,如果您要开发自己的第三方小部件以嵌入其他网站,您实际上应该在自己的iframe中加载来自您自己域的Javascript文件(同样,使用AJAX)

简而言之:

  1. 尝试查看是否可以使用AJAX GET加载资源

  2. 成功加载后使用eval

改善它:

  1. 查看正在发送的缓存控制标头

  2. 如果需要,请查看以其他方式缓存localStorage中的内容

  3. 查看Resig的"降级javascript"以获得更清晰的代码

  4. 查看require.js


小智 5

这个技巧对我有用,虽然我承认这可能不是解决这个问题的最佳方法.而不是尝试这个,你应该看到javascripts没有加载的原因.尝试在服务器等中保留脚本的本地副本,或者与您尝试下载脚本的第三方供应商联系.

无论如何,所以这里是解决方法:1)将变量初始化为false 2)当javascript加载时(使用onload属性)将其设置为true 3)在HTML主体加载后检查变量是真还是假

<html>
  <head>
    <script>
      var scriptLoaded = false;

      function checkScriptLoaded() {
        if (scriptLoaded) {
          // do something here
        } else {
          // do something else here!
        }
      }
    </script>
    <script src="http://some-external-script.js" onload="scriptLoaded=true;" />
  </head>
  <body onload="checkScriptLoaded()">
    <p>My Test Page!</p>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)