So erkennen Sie, ob ein <script> -Tag nicht geladen werden konnte


111

Ich <script>füge einer Seite dynamisch Tags hinzu <head>und möchte feststellen können, ob das Laden auf irgendeine Weise fehlgeschlagen ist - ein 404, ein Skriptfehler im geladenen Skript, was auch immer.

In Firefox funktioniert dies:

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

Dies funktioniert jedoch nicht in IE oder Safari.

Kennt jemand eine Möglichkeit, dies in anderen Browsern als Firefox zum Laufen zu bringen?

(Ich denke nicht, dass eine Lösung, bei der spezieller Code in die .js-Dateien eingefügt werden muss, gut ist. Sie ist unelegant und unflexibel.)


4
Große Website, automatisches Laden von Abhängigkeiten für über Ajax geladene Inhalte. if+ Polling ist eine nervige Kruft, die ich nicht in alle JS stecken möchte.
David

2
Möglicherweise möchten Sie auch nach einem
Ladefehler suchen,

Antworten:


36

Es gibt kein Fehlerereignis für das Skript-Tag. Sie können feststellen, wann es erfolgreich ist, und davon ausgehen, dass es nach einer Zeitüberschreitung nicht geladen wurde:

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

7
Der "Onload" -Listener wird auch dann ausgelöst, wenn ein Javascript-Fehler vorliegt.
Luca Matteis

38
Ja, wenn die Datei geladen wird und ein Fehler in der Datei selbst vorliegt, die Datei jedoch nicht bereitgestellt wird, wird der Onload niemals ausgelöst.
Diodeus - James MacFarlane

1
Ja, stimmt, deshalb habe ich darum gebeten, genauer zu sagen, nach welcher Art von Fehler er sucht.
Luca Matteis

1
Aber das Timeout muss vom Codierer der Seite bereitgestellt werden, oder? Daher wählt der Codierer möglicherweise ein anderes Zeitlimit als der Browser, geht davon aus, dass das Zeitlimit für das Laden des Skripts abgelaufen ist, und lässt es dann etwas später erfolgreich sein. Oder nicht?
Hippietrail

6
Es gibt einen Fehler für das Skript-Tag. Es wird ausgelöst, wenn die Ressource nicht gefunden wird.
Nguyen Tran

24

Wenn Sie sich nur für HTML5-Browser interessieren, können Sie ein Fehlerereignis verwenden (da dies nur zur Fehlerbehandlung dient, sollte es in Ordnung sein, dies nur in Browsern der nächsten Generation für KISS IMHO zu unterstützen).

Aus der Spezifikation:

Wenn der Wert des src-Attributs die leere Zeichenfolge ist oder nicht aufgelöst werden konnte, muss der Benutzeragent eine Task in die Warteschlange stellen, um ein einfaches Ereignis mit dem Namen error am Element auszulösen, und diese Schritte abbrechen.

~

Wenn das Laden zu einem Fehler geführt hat (z. B. ein DNS-Fehler oder ein HTTP 404-Fehler), muss das Ausführen des Skriptblocks nur aus dem Auslösen eines einfachen Ereignisses mit dem Namen error am Element bestehen.

Dies bedeutet, dass Sie keine fehleranfälligen Abfragen durchführen müssen und diese mit den Attributen asynchron und verzögern kombinieren können, um sicherzustellen, dass das Skript das Rendern von Seiten nicht blockiert:

Das Defer-Attribut kann auch dann angegeben werden, wenn das Async-Attribut angegeben ist, damit ältere Webbrowser, die nur Defer (und nicht Async) unterstützen, auf das Defer-Verhalten anstelle des standardmäßigen synchronen Blockierungsverhaltens zurückgreifen.

Mehr unter http://www.w3.org/TR/html5/scripting-1.html#script


1
Gibt es eine Idee für JSONP-Implementierungen, wie sie in jQuery unterstützt werden? Alles, was ich hier über das Erkennen von JSONP-Ladefehlern gelesen habe, besagt, dass es nicht erkannt werden kann, aber ein Timeout kann eine Problemumgehung sein, und einer aktuellen Version von JSONP wurde ein Timeout hinzugefügt. Es scheint, dass diese Antwort etwas Besseres bietet als das Timeout - ist das richtig?
Hippietrail

1
Beispiel möglich bitte?
Rogerdpack

13
<script src="nonexistent.js" onerror="alert('error!')"></script> Geige
Rudey

@ Rudey Uncaught SyntaxError: Unexpected token '<'(im neuesten Chrome)
daleyjem

@daleyjem klingt so, als hätten Sie versucht, ein HTML-Tag in JavaScript einzufügen. <script src="nonexistent.js" onerror="alert('error!')"></script>sollte in Ihre HTML-Datei gehen, nicht JS.
Rudey

21

Das Skript von Erwinus funktioniert hervorragend, ist aber nicht sehr klar codiert. Ich nahm mir die Freiheit, es aufzuräumen und zu entziffern, was es tat. Ich habe diese Änderungen vorgenommen:

  • Sinnvolle Variablennamen
  • Verwendung von prototype.
  • require() verwendet eine Argumentvariable
  • alert()Standardmäßig werden keine Nachrichten zurückgegeben
  • Einige Syntaxfehler und Probleme mit dem Bereich wurden behoben

Nochmals vielen Dank an Erwinus, die Funktionalität selbst ist genau richtig.

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

4
Ich war gezwungen, jQuerys $ .getScript zu verwenden , da diese Funktion für zwischengespeicherte Skripte in MSIE8- leider
fehlschlug

@ZathrusWriter das ist wahrscheinlich eine bessere Idee, danke, dass du uns informiert hast! Sie könnten der URL in diesem Code wahrscheinlich ein zufälliges int hinzufügen, wodurch das Caching entfernt wird.
Aram Kocharyan

2
Ja, Aram, das würde sicherlich den Trick machen, aber es würde auch das Caching des Browsers ungültig machen, so dass sich der Overhead in einem solchen Fall wahrscheinlich nicht wirklich lohnt;)
Zathrus Writer

@ZathrusWriter, wie funktioniert die Funktionsweise der jQuery-Implementierung?
Pacerier

@ Pacerier Entschuldigung, ich bin kein jQuery-Kernentwickler, daher kann ich das nicht wirklich beantworten
Zathrus Writer

18

meine funktionierende saubere Lösung (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);
 })

}

Wie Martin zeigte, so verwendet:

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

ODER

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

1
Verwenden Sie es wie folgt:loaderScript("myscript.js").then(() => { console.log("loaded"); }).catch(() => { console.log("error"); });
Martin Wantke

17

Ich weiß, dass dies ein alter Thread ist, aber ich habe eine nette Lösung für Sie (glaube ich). Es wurde von einer meiner Klassen kopiert, die alle AJAX-Sachen handhabt.

Wenn das Skript nicht geladen werden kann, wird ein Fehlerhandler festgelegt. Wenn der Fehlerhandler jedoch nicht unterstützt wird, wird auf einen Timer zurückgegriffen, der 15 Sekunden lang nach Fehlern sucht.

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

4
... woher kam jQuery?
Naftali aka Neal

1
Und es ist auch eine browserübergreifende Lösung ;-)
Codebeat

1
@Erwinus Ich habe keine Header überprüft, es war nur eine schnelle browserübergreifende Überprüfung, die ich durchgeführt habe, und $ .getScript lief immer ohne Probleme, also blieb ich dabei ... Sie können es gerne selbst versuchen, ich verwende W7 und XAMPP hier
Zathrus Writer

19
Ob dies funktioniert oder nicht, es ist verdammt schmerzhaft zu lesen - geschweige denn zu debuggen. Das ist eine ziemlich schlechte Formatierung und ein wirklich grausamer Codierungsstil. Allein die Benennung der Variablen ist ein Chaos.
Thor84no

7
Wenn Sie den Wert von lesbarem Code nicht sehen (und diesen ohne Änderungen am tatsächlich ausgeführten Code lesbar machen könnten), tut mir jede einzelne Person leid, die mit Ihnen und Ihrem Code arbeiten muss.
Thor84no

10

Um zu überprüfen, ob das Javascript in nonexistant.jskeinen Fehler zurückgegeben hat, müssen Sie eine Variable in http://fail.org/nonexistant.jslike hinzufügenvar isExecuted = true; und dann prüfen, ob sie beim Laden des Skript-Tags vorhanden ist.

Wenn Sie jedoch nur überprüfen möchten, ob die nonexistant.jsRückgabe ohne 404 erfolgt (was bedeutet, dass sie vorhanden ist), können Sie es mit einer isLoadedVariablen versuchen ...

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
    }
}

Dies wird beide Fälle abdecken.


1
Es funktioniert, macht aber nicht wirklich das, was ich will - ein Fehler in diesem Fall würde niemals ein Ereignis auslösen. Ich möchte nicht nach einer Variablen abfragen müssen und, wenn sie nach einigen Sekunden immer noch falsch ist, den On-Fail-Rückruf auslösen.
David

Welche Art von Fehler versuchen Sie abzurufen? Fehler beim Laden? Fehler beim Ausführen des Javascript in nonexstant.js? HTTP-Antwortfehler? Bitte beschreiben Sie genauer.
Luca Matteis

Jedes der oben genannten wäre gut. Ein 404-Fehler ist mir jedoch besonders wichtig.
David

404, was bedeutet, dass Sie überprüfen möchten, ob die Datei nonexistant.js nicht vorhanden ist? Aber wenn ja, möchten Sie überprüfen, ob es keinen Fehler zurückgegeben hat?
Luca Matteis

Hey David, Sie sollten davon ausgehen können, dass das Skript nicht geladen werden konnte, wenn this.readyState / ... nicht wahr ist. Grundsätzlich ein onError.
Cody

7

Ich hoffe, dass dies nicht herabgestuft wird, da es unter besonderen Umständen der zuverlässigste Weg ist, das Problem zu lösen. Jedes Mal, wenn der Server es Ihnen ermöglicht, mithilfe von CORS eine Javascript-Ressource abzurufen ( http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) ) , stehen Ihnen zahlreiche Optionen zur Verfügung.

Die Verwendung von XMLHttpRequest zum Abrufen von Ressourcen funktioniert in allen modernen Browsern, einschließlich IE. Da Sie Javascript laden möchten, steht Ihnen zunächst Javascript zur Verfügung. Sie können den Fortschritt mit dem readyState ( http://en.wikipedia.org/wiki/XMLHttpRequest#The_onreadystatechange_event_listener ) verfolgen . Sobald Sie den Inhalt der Datei erhalten haben, können Sie ihn mit eval () ausführen. Ja, ich habe eval gesagt - da es sich aus Sicherheitsgründen nicht vom normalen Laden des Skripts unterscheidet. Tatsächlich schlägt John Resig eine ähnliche Technik vor, um schönere Tags zu haben ( http://ejohn.org/blog/degrading-script-tags/ ).

Mit dieser Methode können Sie auch das Laden von der Auswertung trennen und Funktionen vor und nach der Auswertung ausführen. Dies ist sehr nützlich, wenn Sie Skripte parallel laden, aber nacheinander auswerten. Dies können Browser problemlos tun, wenn Sie die Tags in HTML platzieren. Sie können dies jedoch nicht tun, indem Sie zur Laufzeit mit Javascript Skripte hinzufügen.

CORS ist auch JSONP zum Laden von Skripten vorzuziehen ( http://en.wikipedia.org/wiki/XMLHttpRequest#Cross-domain_requests ). Wenn Sie jedoch eigene Widgets von Drittanbietern entwickeln, die in andere Websites eingebettet werden sollen, sollten Sie die Javascript-Dateien aus Ihrer eigenen Domain in Ihren eigenen Iframe laden (ebenfalls mit AJAX).

Zusamenfassend:

  1. Versuchen Sie herauszufinden, ob Sie die Ressource mit AJAX GET laden können

  2. Verwenden Sie eval, nachdem es erfolgreich geladen wurde

Um es zu verbessern:

  1. Überprüfen Sie die gesendeten Cache-Control-Header

  2. Prüfen Sie gegebenenfalls, ob Sie den Inhalt in localStorage zwischenspeichern möchten

  3. Schauen Sie sich Resigs "erniedrigendes Javascript" an, um saubereren Code zu erhalten

  4. Check out require.js


Hinweis: Auf diese Weise muss auf dem Ursprungsserver CORS aktiviert sein, was nicht immer der Fall ist oder gesteuert werden kann: |
Rogerdpack

5

Dieser Trick hat bei mir funktioniert, obwohl ich zugebe, dass dies wahrscheinlich nicht der beste Weg ist, um dieses Problem zu lösen. Anstatt dies zu versuchen, sollten Sie sehen, warum die Javascripts nicht geladen werden. Versuchen Sie, eine lokale Kopie des Skripts auf Ihrem Server usw. aufzubewahren, oder wenden Sie sich an den Drittanbieter, von dem Sie versuchen, das Skript herunterzuladen.

Wie auch immer, hier ist die Problemumgehung: 1) Initialisieren Sie eine Variable auf false. 2) Setzen Sie sie auf true, wenn das Javascript geladen wird (mithilfe des onload-Attributs). 3) Überprüfen Sie, ob die Variable true oder false ist, nachdem der HTML-Body geladen wurde

<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>

1
Was ist, wenn das Skript noch geladen wird? Woher wissen Sie, ob die Datei nicht gefunden wurde oder ob Sie nur warten müssen?
osa

Diese Lösung funktioniert in meinen Tests, solange das Skript-Tag direkt in der Dokumentquelle platziert wird. Das Onload-Ereignis des Dokuments wird erst ausgelöst, wenn alle Ressourcen, auf die in der Seitenquelle verwiesen wird, bereits geladen wurden (oder fehlgeschlagen sind). Wenn Sie später Skripte in das DOM einfügen müssen, benötigen Sie einen anderen Ansatz.
Jamey Sharp

Tolle Lösung. Ich verstehe nicht, warum es nicht positiv bewertet wurde.
Lotfi

Dies setzt voraus, dass das Skript synchron geladen wird und alles andere blockiert, was nicht immer der Fall ist. Tatsächlich wird das Laden von Skripten nicht empfohlen.
Vitim.us

4

Hier ist eine weitere JQuery-basierte Lösung ohne Timer:

<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>

Dank an: https://stackoverflow.com/a/14691735/1243926

Beispielnutzung (Originalbeispiel aus der JQuery getScript-Dokumentation ):

<!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>

Netter Sergey! Sie verwenden also .get (), um zu überprüfen, ob die Datei vorhanden ist, und .getScript (), um zu überprüfen, ob sie fehlerfrei ausgeführt / geladen werden kann?
JJWDesign

2
Ja. Soweit ich mich erinnere, weist dieser Ansatz Einschränkungen auf: Sie können nicht viele Skripte auf diese Weise laden - wenn ich mich richtig erinnere, aufgrund domänenübergreifender Skriptbeschränkungen.
osa

Das Problem bei der Verwendung von jQuery (oder einer anderen Bibliothek) besteht darin, dass Sie diese Bibliothek zuerst laden müssen. Wie können Sie also überprüfen, ob diese Bibliothek nicht geladen werden konnte?
Pacerier

Ich habe diesen Ansatz ebenfalls gewählt, empfehle jedoch die Verwendung cache:truefür den Aufruf von $ .getScript (dies ist standardmäßig deaktiviert). Auf diese Weise wird für die meisten Anfragen automatisch die zwischengespeicherte Version für den getScript-Aufruf verwendet (wodurch Sie Latenz sparen)
Laurens Rietveld

2

Dies kann sicher mit Versprechungen geschehen

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

und so verwenden

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

1

Der Grund, warum es in Safari nicht funktioniert, ist, dass Sie die Attributsyntax verwenden. Dies wird jedoch gut funktionieren:

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

... außer im IE.

Wenn Sie überprüfen möchten, ob das Skript erfolgreich ausgeführt wurde, legen Sie einfach eine Variable mit diesem Skript fest und prüfen Sie, ob sie im äußeren Code festgelegt ist.


Für Safari macht das eigentlich keinen Unterschied - der Fehlerbehandler wird immer noch nicht ausgelöst.
David

Ich hatte ein Problem mit einem Onclick-Handler in der Safari und das hat es behoben, also dachte ich, dass es auch für Onerror dasselbe sein würde. Anscheinend nicht ...

1
Sollte dies auch für statisch eingeschlossene Skript-Tags funktionieren oder nur für solche, die dynamisch injiziert werden? Ich habe nur versucht, damit festzustellen, ob jQuery nicht geladen werden konnte, aber es hat nicht funktioniert. Ich bin mir nicht sicher, ob ich es falsch mache oder ob es in diesem Fall einfach nicht funktioniert.
Hippietrail

1
@hippietrail - Ein Ereignis-Listener arbeitet niemals mit statischem HTML <script>. Wenn Sie versuchen, es zuvor hinzuzufügen, wird die Fehlermeldung angezeigt, dass das Tag nicht vorhanden ist. Wenn Sie es anschließend hinzufügen, wird das Skript bereits ausgeführt und hat seine Ereignisse ausgelöst. Die einzige Möglichkeit besteht darin, nach Nebenwirkungen zu suchen, die durch das Skript verursacht werden.

Danke Flussence. Schade, dass dies nur für 404 funktioniert, nicht für JS-Fehler. Das Festlegen einer Variablen im Skript ist nicht immer praktikabel.
Jo Liss

0

Es wurde vorgeschlagen, ein Timeout festzulegen und nach einem Timeout einen Lastfehler anzunehmen.

setTimeout(fireCustomOnerror, 4000);

Das Problem bei diesem Ansatz ist, dass die Annahme auf dem Zufall basiert. Nach Ablauf Ihres Timeouts steht die Anforderung noch aus. Die Anforderung für das ausstehende Skript wird möglicherweise geladen, selbst nachdem der Programmierer angenommen hat, dass das Laden nicht erfolgt.

Wenn die Anforderung abgebrochen werden könnte, könnte das Programm eine Zeit lang warten und dann die Anforderung abbrechen.


0

So habe ich ein Versprechen verwendet, um Ladefehler zu erkennen, die auf dem Fensterobjekt ausgegeben werden:

<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>

0

Der einzige Weg, wie ich mir vorstellen kann, alles zu tun, was du willst, ist ziemlich hässlich. Führen Sie zuerst einen AJAX-Aufruf aus, um den Inhalt der Javascript-Datei abzurufen. Wenn dies abgeschlossen ist, können Sie den Statuscode überprüfen, um zu entscheiden, ob dies erfolgreich war oder nicht. Nehmen Sie dann den responseText aus dem xhr-Objekt und verpacken Sie ihn in einen try / catch, erstellen Sie dynamisch ein Skript-Tag. Für IE können Sie die Texteigenschaft des Skript-Tags in allen anderen Browsern auf den JS-Text setzen Hängen Sie einen Textknoten mit dem Inhalt an das Skript-Tag an. Wenn Code vorhanden ist, der erwartet, dass ein Skript-Tag tatsächlich den Quellspeicherort der Datei enthält, funktioniert dies nicht, sollte jedoch in den meisten Situationen in Ordnung sein.


Diese Lösung ist mit modernen Browsern weitestgehend veraltet.
Simon_Weaver

0

onerror Event

* Update August 2017: onerror wird von Chrome und Firefox ausgelöst. Onload wird von Internet Explorer ausgelöst. Edge feuert weder Fehler noch Last ab. Ich würde diese Methode nicht verwenden, aber es könnte in einigen Fällen funktionieren. Siehe auch

<link> onerror funktioniert nicht im IE

* *

Definition und Verwendung Das Fehlerereignis wird ausgelöst, wenn beim Laden einer externen Datei (z. B. eines Dokuments oder eines Bildes) ein Fehler auftritt.

Tipp: Bei Verwendung auf Audio- / Videomedien treten folgende Ereignisse auf, wenn der Ladevorgang des Mediums gestört wird:

  • onabort
  • onemptied
  • installiert
  • onsuspend

In HTML:

element onerror = "myScript">

Verwenden Sie in JavaScript die Methode addEventListener ():

object.addEventListener ("error", myScript);

Hinweis: Die Methode addEventListener () wird in Internet Explorer 8 und früheren Versionen nicht unterstützt.

Beispiel Führen Sie ein JavaScript aus, wenn beim Laden eines Bildes ein Fehler auftritt:

img src = "image.gif" onerror = "myFunction ()">

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.