wake-up-neo.com

Wie kann ich feststellen, ob ein <script> -Tag nicht geladen wurde?

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

In Firefox funktioniert das:

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, diese Funktion in anderen Browsern als Firefox auszuführen?

(Ich denke nicht, dass eine Lösung, die erfordert, speziellen Code in die .js-Dateien zu schreiben, eine gute Lösung ist. Sie ist unelegant und unflexibel.)

100
David

Es gibt kein Fehlerereignis für das Skript-Tag. Sie können feststellen, wann es erfolgreich war, und davon ausgehen, dass es nach einem Timeout nicht geladen wurde

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

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

Aus der Spezifikation:

Wenn der Wert des Attributs src der Wert ist leere Zeichenfolge oder wenn es nicht sein konnte gelöst, dann muss der Benutzeragent Eine Aufgabe in eine Warteschlange stellen, um ein einfaches Ereignis auszulösen benannt error am Element und brechen Sie diese Schritte ab.

~

Wenn das Laden zu einem Fehler geführt hat (z. B. ein DNS-Fehler oder ein HTTP-Fehler 404), muss der Skriptblock ausgeführt werden bestehen nur aus dem Auslösen eines einfachen Ereignisses benannter Fehler am Element.

Das bedeutet, dass Sie keine fehleranfälligen Abfragen durchführen müssen und diese mit dem Attribut async und defer kombinieren können, um sicherzustellen, dass das Skript das Seiten-Rendering nicht blockiert:

Das Defer-Attribut kann angegeben werden auch wenn das async-Attribut .__ ist. angegeben, um das ältere Web zu verursachen Browser, die nur Zeitgeber unterstützen (und nicht asynchron), um auf den Zeitgeber zurückzugreifen Verhalten statt des synchronen Blockierverhalten, das ist der Standard.

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

21
andrerom

Das Skript von Erwinus funktioniert gut, ist aber nicht sehr klar codiert. Ich erlaubte mir, es aufzuräumen und zu entschlüsseln, was es tat. Ich habe diese Änderungen vorgenommen:

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

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

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

Ich weiß, dass dies ein alter Thread ist, aber ich habe eine schöne Lösung für Sie (denke ich). Es ist von einer Klasse von mir kopiert, die alles AJAX erledigt.

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 auf Fehler prüft.

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

Um zu überprüfen, ob das Javascript in nonexistant.js keinen Fehler zurückgegeben hat, müssen Sie eine Variable in http://fail.org/nonexistant.js wie var isExecuted = true; hinzufügen und dann prüfen, ob diese vorhanden ist, wenn das Skripttag geladen wird.

Wenn Sie jedoch nur überprüfen möchten, dass der nonexistant.js ohne 404 zurückgegeben wurde (dh, es existiert), können Sie es mit einer isLoaded-Variablen 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.

9
Luca Matteis

Ich hoffe, dass dies nicht abgelehnt wird, weil es unter besonderen Umständen die zuverlässigste Art ist, das Problem zu lösen. Jedes Mal, wenn der Server Ihnen erlaubt, eine Javascript-Ressource mit CORS ( http://en.wikipedia.org/wiki/Cross-Origin_resource_sharing ) zu erhalten, haben Sie eine Vielzahl von Optionen.

Die Verwendung von XMLHttpRequest zum Abrufen von Ressourcen funktioniert in allen modernen Browsern, einschließlich IE. Da Sie Javascript laden möchten, steht Ihnen in erster Linie Javascript zur Verfügung. Sie können den Fortschritt mithilfe von readyState ( http://en.wikipedia.org/wiki/XMLHttpRequest#The_onreadystatechange_event_listener ) verfolgen. Nachdem Sie den Inhalt der Datei erhalten haben, können Sie ihn schließlich mit eval () ausführen. Ja, ich habe eval gesagt - weil es sicherheitstechnisch nicht anders ist, als das Skript normal zu laden. 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 vom eval trennen und Funktionen ausführen, bevor und nachdem das eval geschieht. Dies ist sehr nützlich, wenn Skripte parallel geladen werden, aber sie nacheinander ausgewertet werden. Browser können dies problemlos tun, wenn Sie die Tags in HTML einfügen. Lassen Sie sich jedoch nicht zur Laufzeit mit JavaScript Skripts hinzufügen.

CORS ist JSONP auch 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 von Ihrer eigenen Domäne in Ihren eigenen iframe laden (wiederum 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. Überprüfen Sie gegebenenfalls den Cache in localStorage, wenn Sie dies benötigen

  3. Sehen Sie sich Resig's "degradierendes Javascript" für saubereren Code an

  4. Check out requir.js

6

Dieser Trick funktionierte für mich, obwohl ich zugeben muss, dass dies wahrscheinlich nicht der beste Weg ist, 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 das Skript herunterladen möchten.

Wie dem auch sei, 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) Prüfen Sie, ob die Variable einmal true oder false ist Der HTML-Body wurde geladen

<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>
5
Gautam Tandon

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>

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

Verwendungsbeispiel (Originalbeispiel aus 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>
5
osa

meine saubere Arbeitslö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);
 })

}
4
pery mimon

Nun, der einzige Weg, an den ich denken kann, um alles zu tun, was Sie wollen, 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 ein try/catch. Erstellen Sie dynamisch ein Script-Tag. Für IE können Sie die Texteigenschaft des Script-Tags in allen anderen Browsern auf den JS-Text setzen sollte in der Lage sein, einen Textknoten mit dem Inhalt an das Skript-Tag anzuhängen. Wenn es einen Code gibt, der erwartet, dass ein Script-Tag tatsächlich den SRC-Speicherort der Datei enthält, funktioniert dies nicht. In den meisten Situationen ist dies jedoch ausreichend. Hässlich, wird aber funktionieren. Sie können sich mein schnelles und schmutziges Beispiel hier ansehen: http://pastie.org/829775

2
nickjsify

Dies kann mit Versprechungen sicher durchgeführt werden

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

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

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

... außer im IE.

Wenn Sie das erfolgreich ausgeführte Skript überprüfen möchten, setzen Sie einfach eine Variable mit diesem Skript und prüfen Sie, ob es im äußeren Code festgelegt ist.

1
user42092

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

sie können, falls möglich, eine Funktion in die Java-Skriptdatei aufnehmen, z. B. check () {var report; report = true}. Führen Sie dann diese Funktion aus Wenn die Funktion nicht ausgeführt wird oder nicht antwortet, wird das Skript wahrscheinlich nicht geladen Das Skript kann nicht verwendet werden. Das Attribut onload ist ebenfalls nützlich. 

0
Abhishek Mathur

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

setTimeout(fireCustomOnerror, 4000);

Das Problem bei diesem Ansatz ist, dass die Annahme auf dem Zufall beruht. Nachdem Ihr Timeout abgelaufen ist, steht die Anforderung noch aus. Die Anforderung für das ausstehende Skript wird möglicherweise geladen, selbst nachdem der Programmierer davon ausgegangen ist, dass das Laden nicht stattfindet.

Wenn die Anforderung abgebrochen werden konnte, konnte das Programm eine Zeitspanne warten und die Anforderung anschließend abbrechen. 

0
Garrett

onerror Event

* Update August 2017: Fehler werden 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 in IE

*

Definition und Verwendung Das Ereignis onerror 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
  • versuchten
  • installiert
  • onsuspend

In HTML:

element onerror = "myScript">

In JavaScript mit der Methode addEventListener ():

object.addEventListener ("error", myScript);

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

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

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

0
William Monahan