WD_2012: JavaScript-Problem - Externes Skript laden

Hallo zusammen,

für eine dynamische Einbindung einer externen JavaScript-Datei setze ich zur Laufzeit aus einer JavaScript-Funktion heraus das src-Attribut für ein (vorher im header-Teil definierte) script-Objekt. Das funktionierte in Firefox 3.. und funktioniert immer noch im IE, aber nicht mehr in neuen Firefox-Versionen (4.0 - 14.0).

Folgendes einfaches Beispiel zeigt das Problem:

test.html

<html>
  <head>
   <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
   <script type="text/javascript" src="fcts.js"></script>
   <script type="text/javascript" id="myscriptObject"></script>
  </head>

<body>
    <script> change_src() </script>
    <script> test() </script>
  </body>
</html>

fcts.js

function change_src(){
  document.getElementById("myscriptObject").src = "fcts2.js";
}

fcts2.js

function test(){
  alert("Hello");
}

Die Fehlerkonsole sagt: 'ReferenceError: test is not defined'.

Kennt jemand die Ursache dafür, dass die Funktion test() nicht gefunden wird? Und vor allem die Lösung?

  1. Hi,

    Die Fehlerkonsole sagt: 'ReferenceError: test is not defined'.

    Kennt jemand die Ursache dafür, dass die Funktion test() nicht gefunden wird?

    Das Laden des Scriptes dauert „lange“ – während der JavaScript-Interpreter *sofort* nach change_src versucht test aufzurufen.

    Und vor allem die Lösung?

    Auch externe Ressourcen wie Scripte feuern auf die eine oder andere Art und Weise load-Events oder vergleichbares; es ist nur nicht ganz so leicht, das browserübergreifend hin zu basteln.

    Mein Tipp wäre, verwende eines der gängigen JS-Frameworks (oder einen stand-alone Scriptloader) – die haben eigentlich alle Methoden, um Scripte asynchron nachzuladen und dann mit einer Handler-Funktion auf das Ereignis „Fertig mit Script nachladen!“ zu reagieren.

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. Hallo ChrisB, hallo WD_2012,

      Mein Tipp wäre, verwende eines der gängigen JS-Frameworks (oder einen stand-alone Scriptloader) – die haben eigentlich alle Methoden, um Scripte asynchron nachzuladen und dann mit einer Handler-Funktion auf das Ereignis „Fertig mit Script nachladen!“ zu reagieren.

      meine Version sieht z.Zt. so aus:

        
      LoadScript = function(url,callback) {  
        var scr = document.createElement('script');  
        scr.type = "text/javascript";  
        scr.async = "async";  
        if(typeof(callback)=="function") {  
          scr.onloadDone = false;  
          scr.onload = function() {  
            if ( !scr.onloadDone ) {  
              scr.onloadDone = true;  
              callback();  
            }  
          };  
          scr.onreadystatechange = function() {  
            if ( ( "loaded" === scr.readyState || "complete" === scr.readyState ) && !scr.onloadDone ) {  
              scr.onloadDone = true;  
              callback();  
            }  
          }  
        }  
        scr.src = url;  
        document.getElementsByTagName('head')[0].appendChild(scr);  
      } // LoadScript  
      
      

      Gruß, Jürgen

      1. Hallo zusammen,

        vielen Dank für Eure Antworten. Ich werde es versuchen.

        Allerdings habe ich schon an das asynchrone Laden als Ursache gedacht und versucht, die Ausführung der Funktion test() über setTimeout() zu verzögern. Das hat nichts gebracht. Es hat auch nicht geholfen, die async-Eigenschaft des script-Objekts myscriptObject auf false zu setzen, um das asynchrone Laden zu unterbinden. Dafür ist dieses Attribut aber doch da, oder?

        Und im IE funktioniert das Ganze ohne Probleme.

        Viele Grüße,
        WD_2012

        1. Hallo WD_2012,

          hast du mal ein Online-Beispiel? Hast du mal eine Nachladeversion mit Callback-Funktion ausprobiert?

          Gruß, Jürgen

          1. Hallo,

            @ChrisB:

            die Nutzung der setTimeout-Funktion war nur zum Test gedacht. Wenn es ein Problem ist, das durch asynchrones Laden zustande kommt, dann sollte theoretisch die Funktion test() vom Parser gefunden werden, wenn die Verzögerung groß genug gewählt wird.

            @JurgenB:

            ich habe leider kein Online-Beispiel. Die drei Dateien befinden sich auf meinem lokalen Rechner. Diese haben allerdings exakt den vorher angegebenen Inhalt, so dass du den Quellcode direkt kopieren kannst, um sie auf deinem Rechner zu erzeugen.

            Die Nutzung der Callback-Funktion habe ich ausprobiert. Das hat nicht funktioniert. Konnte auch nicht, da die Funktion test() bei der Ausführung der Funktion LoadScript() noch nicht bekannt ist. Das heißt, die Routine wird in den ersten if-Block (Typabfrage) nie reingehen, um die übergebene callback-Funktion aufzurufen. Und wenn die if-Abfrage entfernt wird, um den callback-Aufruf doch zu erzwingen, dann sagt die Fehlerkonsole erwartungsgemäß "TypeError: callback is not a function".

            Gibt es die Möglichkeit, eine synchrone Ausführung des Codes festzulegen, damit garantiert werden kann, dass das Skript fcts2.js geladen wird, bevor es zum Aufruf der Funktion test() kommt?

            Gruß und Danke,
            WD_2012

            1. Hallo WD_2012,

              versuch mal statt

              LoadScript("test.js",test)

              wo auf das noch nicht vorhandene "test" zugegriffen wird, diese Version

              LoadScript("test.js",function(){test()})

              hier wird eine anonyme Funktion als Callback-Funktion übergeben. Wenn diese dann aufgerufen wird, ist test bekannt.

              Gruß, Jürgen

              1. Hallo JürgenB,

                ja, das hat funktioniert.

                Weitere Lösung, die ich gefunden habe, besteht darin, ein neues script-Objekt über die Funktion document.write() zu generieren:

                function changesource(){
                 document.write("<script src='fcts2.js'></script>");
                }.

                Diese bewirkt ein blockierendes Verhalten, so dass das Skript fcts2.js auf jeden Fall vor dem Aufruf der Funkion test() eingelesen wird.

                Interessant wäre auch zu wissen, warum die Verzögerung der Ausführung von test() mit setTimeout() nicht funktionierte. Und auch warum das Setzen des src-Attributs des script-Objekts myscriptObject auch mit document.write() nicht zum gewünschten Ergebnis führt.

                Danke an alle für Eure Hilfe.

                Gruß,
                WD_2012

                1. Hallo WD_2012,

                  Weitere Lösung, die ich gefunden habe, besteht darin, ein neues script-Objekt über die Funktion document.write() zu generieren:

                  das funktioniert aber nur, wenn die Seite noch nicht fertig geladen/gerendert ist. Danach überschreibt document.write die Seite.

                  Diese bewirkt ein blockierendes Verhalten, ...

                  das man meistens nicht will.

                  Interessant wäre auch zu wissen, warum die Verzögerung der Ausführung von test() mit setTimeout() nicht funktionierte.

                  Da habe ich auch keine Ahnung. Wie lange hast du denn gewartet?
                  Aber da du nicht weist, wie schnell der Rechner des Besuchers, die Internetverbindung oder der Server sind, ist setTimeout zum Warten auf onload immer eine schlechte Idee.

                  Gruß, Jürgen

                  1. Hallo Jurgen,

                    das blockierende Verhalten ist in diesem Fall gewollt.

                    Da habe ich auch keine Ahnung. Wie lange hast du denn gewartet?

                    Gewartet habe ich 10 Sekunden. Der Rest der Seite war längst aufgebaut (ich habe zum Test nach dem Aufruf der Funktion setTimeout() noch eine Ausgabe direkt in die Seite über den <p>-Tag eingebaut).

                    Aber da du nicht weist, wie schnell der Rechner des Besuchers, die Internetverbindung oder der Server sind, ist setTimeout zum Warten auf onload immer eine schlechte Idee.

                    Die Nutzung der Funktion setTimeout() sollte nur zur Problemanalyse dienen und zeigen, dass es sich hierbei um ein Problem handelt, das durch asynchrones Laden hervorgerufen wird. Anscheinend tritt hier jedoch noch ein anderer Effekt zu Tage, der der erwarteten Ausführung einen Strich durch die Rechnung zieht.

                    Gruß,
                    WD_2012

        2. Hi,

          Allerdings habe ich schon an das asynchrone Laden als Ursache gedacht und versucht, die Ausführung der Funktion test() über setTimeout() zu verzögern. Das hat nichts gebracht.

          Ist auch unsinnig – du wirst nie wissen, wie lange ein HTTP-Request effektiv in der jeweiligen Umgebung dauern wird.

          Es hat auch nicht geholfen, die async-Eigenschaft des script-Objekts myscriptObject auf false zu setzen, um das asynchrone Laden zu unterbinden. Dafür ist dieses Attribut aber doch da, oder?

          Nein.
          async und defer beziehen sich in erster Linie darauf, wie bereits von Anfang an im HTML-Dokument vorhandene Script-Einbindungen das Parsen des Dokumentes beeinflussen sollen.

          MfG ChrisB

          --
          RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?