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

我正在动态地将 <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);

然而,这在 IE 或 Safari 中不起作用。

有没有人知道除了 Firefox 之外的其他浏览器也可以这样工作的方法?

(我不认为需要将特殊代码放在。Js 文件是个不错的选择。它既不优雅又不灵活。)

151990 次浏览

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

<script type="text/javascript" onload="loaded=1" src="....js"></script>

要检查 nonexistant.js中的 javascript 是否没有返回错误,您必须在 http://fail.org/nonexistant.js中添加一个变量,比如 var isExecuted = true;,然后检查在加载 script 标记时它是否存在。

但是,如果您只想检查返回的 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
}
}

这两个案子都包括在内。

它在 Safari 中不起作用的原因是因为你使用了属性语法:

script_tag.addEventListener('error', function(){/*...*/}, true);

除了 IE。

如果您想检查成功执行的脚本,只需使用该脚本设置一个变量,并检查它是否在外部代码中设置。

建议设置一个超时,然后在超时后假设负载失败。

setTimeout(fireCustomOnerror, 4000);

这种方法的问题在于,这种假设是基于偶然性的。超时过期后,请求仍未处理。对挂起的脚本的请求可能会加载,即使在程序员假定加载不会发生之后也是如此。

如果可以取消请求,那么程序可以等待一段时间,然后取消请求。

我唯一能想到的做你想做的事的方法就是丑陋。首先执行一个 AJAX 调用来检索 Javascript 文件内容。完成后,您可以检查状态代码以确定是否成功。然后从 xhr 对象获取 response seText 并将其包装在 try/catch 中,动态创建一个 script 标记,对于 IE,您可以将 script 标记的 text 属性设置为 JS 文本,在所有其他浏览器中,您应该能够将内容附加到 script 标记的文本节点。如果有任何代码期望 script 标记实际包含文件的 src 位置,那么这种方法不起作用,但是在大多数情况下应该没有问题。

更新2021: 现在所有的浏览器都在脚本标签上支持 onerror="",例如:

2010年的原始评论:

如果您只关心 html5浏览器,则可以使用错误事件。

规格说明:

如果 src 属性的值是 空字符串或如果它不能是 解决,则用户代理必须 将任务排队以激发简单事件 在元素处命名为 错误,并且 终止这些步骤。

(...)

如果加载导致错误(针对 例如 DNS 错误或 HTTP404 执行脚本块必须 只需要触发一个简单的事件 元素的命名错误。

这意味着您不必执行任何容易出错的轮询,并且可以将其与异步和 Defer 属性组合起来,以确保脚本不会阻塞页面呈现:

可以指定 Defer 属性 即使异步属性是 指定,以导致遗留 Web 只支持延迟(和 (非异步)回到延迟 行为,而不是同步 默认的阻塞行为。

更多关于 http://www.w3.org/TR/html5/scripting-1.html#script的资料

我知道这是一个老线程,但我有一个很好的解决方案给你(我想)。它是从我的一个类复制的,处理所有 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');
});
});

这个方法对我很有效,尽管我承认这可能不是解决这个问题的最好方法。与其尝试这样做,不如看看为什么 javascript 没有加载。尝试在您的服务器中保留脚本的本地副本,或者从您试图下载脚本的第三方供应商那里检查。

不管怎样,这里有个解决办法: 1)将变量初始化为 false 2)在 javascript 加载时将其设置为 true (使用 onload 属性) 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>

我希望这不会遭到否决,因为在特殊情况下,这是解决问题最可靠的方法。任何时候服务器允许您使用 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 在加载脚本(http://en.wikipedia.org/wiki/XMLHttpRequest#Cross-domain_requests)方面也优于 JSONP。但是,如果您正在开发自己的第三方小部件以嵌入到其他站点中,那么您实际上应该在自己的 iframe 中从自己的域加载 Javascript 文件(同样,使用 AJAX)

简而言之:

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

  2. 在成功加载 eval 后使用 eval

为了改进它:

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

  2. 如果需要,可以考虑在 localStorage 中以其他方式缓存内容

  3. 查看 Resig 的“降级 javascript”来获得更清晰的代码

  4. 看看这个

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

下面是另一个没有定时器的基于 JQuery 的解决方案:

<script type="text/javascript">
function loadScript(url, onsuccess, onerror) {
$.get(url)
.done(function() {
// File/url exists
console.log("JS Loader: file exists, executing $.getScript "+url)
$.getScript(url, function() {
if (onsuccess) {
console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
onsuccess();
console.log("JS Loader: done with onsuccess() for " + url);
} else {
console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
}
});
}).fail(function() {
// File/url does not exist
if (onerror) {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
onerror();
console.error("JS Loader: done with onerror() for " + url);
} else {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
}
});
}
</script>

感谢: Https://stackoverflow.com/a/14691735/1243926

样本用量(来自 JQuery getScript 文档的原始样本) :

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery.getScript demo</title>
<style>
.block {
background-color: blue;
width: 150px;
height: 70px;
margin: 10px;
}
</style>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>


<button id="go">&raquo; Run</button>
<div class="block"></div>


<script>




function loadScript(url, onsuccess, onerror) {
$.get(url)
.done(function() {
// File/url exists
console.log("JS Loader: file exists, executing $.getScript "+url)
$.getScript(url, function() {
if (onsuccess) {
console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
onsuccess();
console.log("JS Loader: done with onsuccess() for " + url);
} else {
console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
}
});
}).fail(function() {
// File/url does not exist
if (onerror) {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
onerror();
console.error("JS Loader: done with onerror() for " + url);
} else {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
}
});
}




loadScript("https://raw.github.com/jquery/jquery-color/master/jquery.color.js", function() {
console.log("loaded jquery-color");
$( "#go" ).click(function() {
$( ".block" )
.animate({
backgroundColor: "rgb(255, 180, 180)"
}, 1000 )
.delay( 500 )
.animate({
backgroundColor: "olive"
}, 1000 )
.delay( 500 )
.animate({
backgroundColor: "#00f"
}, 1000 );
});
}, function() { console.error("Cannot load jquery-color"); });




</script>
</body>
</html>

我的工作清洁解决方案(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);
})


}

正如马丁所指出的,这样使用:

const event = loaderScript("myscript.js")
.then(() => { console.log("loaded"); })
.catch(() => { console.log("error"); });


或者

try{
await loaderScript("myscript.js")
console.log("loaded");
}catch{
console.log("error");
}

这可以通过承诺安全地完成

    function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
    

script.onload = () => resolve(script);
script.onerror = () => reject(new Error("Script load error: " + src));
    

document.head.append(script);
});
}

像这样使用

    let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js");
    

promise.then(
script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);

这就是我如何使用承诺来检测在窗口对象上发出的加载错误:

<script type='module'>
window.addEventListener('error', function(error) {
let url = error.filename
url = url.substring(0, (url.indexOf("#") == -1) ? url.length : url.indexOf("#"));
url = url.substring(0, (url.indexOf("?") == -1) ? url.length : url.indexOf("?"));
url = url.substring(url.lastIndexOf("/") + 1, url.length);
window.scriptLoadReject && window.scriptLoadReject[url] && window.scriptLoadReject[url](error);
}, true);
window.boot=function boot() {
const t=document.createElement('script');
t.id='index.mjs';
t.type='module';
new Promise((resolve, reject) => {
window.scriptLoadReject = window.scriptLoadReject || {};
window.scriptLoadReject[t.id] = reject;
t.addEventListener('error', reject);
t.addEventListener('load', resolve); // Careful load is sometimes called even if errors prevent your script from running! This promise is only meant to catch errors while loading the file.
}).catch((value) => {
document.body.innerHTML='Error loading ' + t.id + '! Please reload this webpage.<br/>If this error persists, please try again later.<div><br/>' + t.id + ':' + value.lineno + ':' + value.colno + '<br/>' + (value && value.message);
});
t.src='./index.mjs'+'?'+new Date().getTime();
document.head.appendChild(t);
};
</script>
<script nomodule>document.body.innerHTML='This website needs ES6 Modules!<br/>Please enable ES6 Modules and then reload this webpage.';</script>
</head>


<body onload="boot()" style="margin: 0;border: 0;padding: 0;text-align: center;">
<noscript>This website needs JavaScript!<br/>Please enable JavaScript and then reload this webpage.</noscript>

一个错误事件

* 2017年8月更新: onerror 被 Chrome 和 Firefox 触发。装载由 Internet Explorer 发射。Edge 既不触发错误,也不触发加载。我不会使用这种方法,但它可以在某些情况下工作。参见

< link > onerror do not work in IE

*

定义及用法 如果在加载外部文件(例如文档或图像)时发生错误,则会触发 onerror 事件。

提示: 当在音频/视频媒体上使用时,当媒体加载过程受到某种干扰时发生的相关事件是:

  • 终止任务
  • 空的
  • 启动了
  • 悬挂

HTML:

Element onerror = “ myScript”>

在 JavaScript 中,使用 addEventListener ()方法:

AddEventListener (“ error”,myScript) ;

注意: addEventListener ()方法在 Internet Explorer 8和早期版本中不受支持。

例子 如果加载图像时发生错误,执行 JavaScript:

Img src = “ image.gif”onerror = “ myFunction ()”>

这不需要 jquery,不需要异步加载脚本,不需要计时器,也不需要加载的脚本设置值。我已经在 FF、 Chrome 和 Safari 中测试过了。

<script>
function loadScript(src) {
return new Promise(function(resolve, reject) {


let s = window.document.createElement("SCRIPT");


s.onload = () => resolve(s);
s.onerror = () => reject(new Error(src));
s.src = src;
// don't bounce to global handler on 404.
s.addEventListener('error', function() {});
window.document.head.append(s);
});
}
                        

let successCallback = (result) => {
console.log(scriptUrl + " loaded.");
}
                        

let failureCallback = (error) => {
console.log("load failed: " + error.message);
}
                        

loadScript(scriptUrl).then(successCallback, failureCallback);
</script>