2021年更新:
现在所有的浏览器都支持在脚本标签上使用onerror=""
,例如:
<script src="nonexistent.js" onerror="alert('error!')"></script>
2010年原始评论:
如果只关心 HTML5 浏览器,您可以使用 error 事件。
根据规范:
如果 src 属性的值是空字符串,或者无法解析,则用户代理必须在元素上排队一个名为 error 的简单事件,并中止这些步骤。
(...)
如果加载导致错误(例如 DNS 错误或 HTTP 404 错误),则执行脚本块必须仅包括在元素上触发名为 error 的简单事件。
这意味着您不必进行任何容易出错的轮询,并且可以结合 async 和 defer 属性来确保脚本不会阻塞页面渲染:
即使指定了 async 属性,也可以指定 defer 属性,以使仅支持 defer(而不是 async)的旧 Web 浏览器回退到 defer 行为,而不是同步阻止行为。
Uncaught SyntaxError: Unexpected token '<'
(在最新版的Chrome中) - daleyjem<script src="nonexistent.js" onerror="alert('error!')"></script>
应该放在你的 HTML 文件中,而不是 JS 文件中。 - Rudey脚本标签没有错误事件。您可以在成功时得知它并在超时后假定它未加载:
<script type="text/javascript" onload="loaded=1" src="....js"></script>
error
事件——因为错误地陈述“脚本标签没有错误事件”而被点负。请更新或删除这个过时/错误的答案。 - mindplay.dk我的清洁解决方案(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");
}
loaderScript("myscript.js").then(() => { console.log("loaded"); }).catch(() => { console.log("error"); });
- Martin Wantkeprototype
require()
使用参数变量alert()
消息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');
}
});
});
我知道这是一个老的线程,但是我有一个不错的解决方案(我觉得)。它是从我的一个课程中复制而来,该课程处理所有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');
});
});
要检查 nonexistant.js
中的 JavaScript 是否没有返回错误,您需要在 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
}
}
这将同时涵盖两种情况。
这种方法还允许您将加载与 eval 分离,并在 eval 发生之前和之后执行函数。当并行加载脚本但逐个评估它们时,它变得非常有用 - 当您将 标签放入HTML中时,浏览器可以轻松完成此操作,但是通过使用Javascript在运行时添加脚本时则不行。
对于加载脚本(http://en.wikipedia.org/wiki/XMLHttpRequest#Cross-domain_requests),CORS也优于JSONP。但是,如果您正在开发自己的第三方小部件以嵌入到其他网站中,则应该在您自己的iframe中从您自己的域加载Javascript文件(同样使用AJAX)。
简而言之:
尝试使用 AJAX GET 加载资源
在成功加载后使用 eval
为了改进它:
查看发送的缓存控制标头
如果需要,考虑将内容缓存在 localStorage 中
查看 Resig 的“退化javascript”以获得更清晰的代码
查看 require.js
<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;" />
这可以通过使用Promise来安全地完成
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}`)
);
这里有另一种基于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://dev59.com/s3A65IYBdhLWcg3wxBir#14691735
示例用法(来自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">» 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>
cache:true
(默认情况下关闭)。这样,在大多数请求中,它会自动使用 getScript 调用的缓存版本(节省您的延迟时间)。 - Laurens Rietveld
if
+轮询是让我不想在所有JS中都加入的烦人的操作。 - David