Ralf: onload bei dynamisch erzeugtem IFRAME im IE funktioniert nicht

Hallo!

Ich habe ein Script, welches dynamisch ein IFRAME Element erzeugt und dann dafür ein onload-Event setzt. Das funktioniert auch im Firefox und Opera, aber nicht im IE. Hier der Code:

      var nf = document.createElement("IFRAME");  
      document.body.appendChild(nf);  
      nf.onload = function() {alert('geladen');};  
      nf.src = "test.html";  

Im IE kommt keine Nachricht - Fehler, Restriktion, Unvermögen?

Ralf

  1. Ich habe nun selbst eine Umgehung/Lösung gefunden. Da ich mit dem prototype Framework arbeite, habe ich folgendes eingesetzt:

      
          Event.observe(nf, 'load', function() {alert("geladen");});  
    
    

    Funktioniert auch im IE und somit liegt der Verdacht nah, dass ich es hätte anders lösen können. Aber wie?

    Ralf

    1. Funktioniert auch im IE und somit liegt der Verdacht nah, dass ich es hätte anders lösen können. Aber wie?

      Ich hab mal die Stelle rausgesucht hier wird der Event registriert:

        _observeAndCache: function(element, name, observer, useCapture) {  
          if (!this.observers) this.observers = [];  
          if (element.addEventListener) {  
            this.observers.push([element, name, observer, useCapture]);  
            element.addEventListener(name, observer, useCapture);  
          } else if (element.attachEvent) {  
            this.observers.push([element, name, observer, useCapture]);  
            element.attachEvent('on' + name, observer);  
          }  
        },  
      
      

      Die Lösung ist also mit attachEvent()

      Struppi.

      --
      Javascript ist toll (Perl auch!)
      1. Die Lösung ist also mit attachEvent()

        Vielen Dank! Ich müsste aber dann die Funktion von _observeAndCache nachbilden - also auf addEventListener prüfen und je nach Ergebnis verfahren.
        Da ich zukünftig eher weitere Funktionen von prototype nutzen werde, kann ich es auch so belassen. Außerdem führt prototype für die registrierten Handler beim unload automatisch eine Deregistrierung durch (für den IE).

        Ralf

        1. Da ich zukünftig eher weitere Funktionen von prototype nutzen werde, kann ich es auch so belassen. Außerdem führt prototype für die registrierten Handler beim unload automatisch eine Deregistrierung durch (für den IE).

          so sieht's aus. In dem Falle ging's ja nur um den IE, d.h. es hätte gereicht dem IE so den Event zu präsentieren (mit dem Nachteil entweder memomry leaks oder eine unload Funktion schreiben zu müssen). Aber wenn du eh prototype verwenden willst, würde ich mir darüber keine Gedanken machen.

          Nur weil du es Wissen wolltest wie es geht (ich auch ;-) )

          Struppi.

          --
          Javascript ist toll (Perl auch!)
          1. Nur weil du es Wissen wolltest wie es geht (ich auch ;-) )

            Vielleicht hast du eine Idee zu einem Nachfolgeproblem, was sich nun aufgetan hat. Vermutlich fehlt mir da noch das Verständnis. Hier ein Ausschnitt:

              
              var vorlagen = document.getElementsByClassName("vorlage"); //prototype.js  
              if (!vorlagen.length) return;  
              for (var i=0; i<vorlagen.length; i++) {  
                var prodid = vorlagen[i].className.match(/\d+/);  
                if (prodid) {  
                  var nf = document.createElement("IFRAME");  
                  document.body.appendChild(nf);  
                  nf.src = "produkt.aspx?produktid="+prodid;  
                  Event.observe(nf, 'load', function() { //prototype.js  
                    var v = nf.contentWindow.document.getElementById("vorlage");  
                    if (v) vorlagen[i].appendChild(v);  
                    });  
                  }  
            //    break;  
                }  
            
            

            In dem Code soll für alle Elemente mit class=vorlage ein Ausschnitt aus einem Dokument eingesetzt werden, welches per IFRAME geladen wird. Das funktioniert auch, wenn es nur ein Element mit class=vorlage gibt (bzw. das break nicht auskommentiert ist). Gibt es aber mehrere, bringt der Firefox "vorlagen[i] has no properties" für die anonyme Funktion.

            Irgendwie verstehe ich auch, dass durch den nächsten Schleifendurchlauf der Wert weg ist, aber wie mache ich es richtig?

            Ralf

            1. Vielleicht hast du eine Idee zu einem Nachfolgeproblem, was sich nun aufgetan hat. Vermutlich fehlt mir da noch das Verständnis. Hier ein Ausschnitt:

              var vorlagen = document.getElementsByClassName("vorlage"); //prototype.js
                if (!vorlagen.length) return;
                for (var i=0; i<vorlagen.length; i++) {
                  var prodid = vorlagen[i].className.match(/\d+/);
                  if (prodid) {
                    var nf = document.createElement("IFRAME");
                    document.body.appendChild(nf);
                    nf.src = "produkt.aspx?produktid="+prodid;
                    Event.observe(nf, 'load', function() { //prototype.js
                      var v = nf.contentWindow.document.getElementById("vorlage");
                      if (v) vorlagen[i].appendChild(v);
                      });
                    }
              //    break;
                  }

                
              Das Problme ist das i in der anonymen Funktion immer den letzten Wert hat (also  vorlagen.length). Ein einfacher Workaround wäre nf als Attribut i mitzugeben, also z.b. nf.i = i und in der Funktion als nf.i verwenden.  
              Ob das aber geht weiß ich nicht, da für nf das Problem ebenfalls auftritt.  
                
              Im Prinzip bietet aber prototype gerade für sowas vielfältige Funktionen an, aber ich hab damit noch nie gearbeitet und weiß nicht wie. Auf jeden Fall gibt es dort allein schon für [Arrays](http://www.prototypejs.org/api/array) (die Rückgabe von getElementsByClassName) vielfältige Funktionen.  
              Ich würde mir die Api von prototype mal genauer anschauen.  
                
              Wobei ich mich Frage was du da vorhast, sieht ja nach einer Anwendung für AJAX aus oder was machst du mit den ganzen iframes?  
                
              Struppi.
              
              -- 
              [Javascript ist toll](http://javascript.jstruebig.de/) (Perl auch!)
              
              1. Das Problme ist das i in der anonymen Funktion immer den letzten Wert hat (also  vorlagen.length). Ein einfacher Workaround wäre nf als Attribut i mitzugeben, also z.b. nf.i = i und in der Funktion als nf.i verwenden.
                Ob das aber geht weiß ich nicht, da für nf das Problem ebenfalls auftritt.

                Wie kann ich denn Variablen an die anonyme Funktion übergeben? Geht das genauso wie sonst auch? Also:

                  
                Event.observe(nf, 'load', function(p1,p2) { ...  
                
                

                Im Prinzip bietet aber prototype gerade für sowas vielfältige Funktionen an, aber ich hab damit noch nie gearbeitet und weiß nicht wie. Auf jeden Fall gibt es dort allein schon für Arrays (die Rückgabe von getElementsByClassName) vielfältige Funktionen.
                Ich würde mir die Api von prototype mal genauer anschauen.

                Daran verzweifliche ich gerade, weil mein seit über 30 Jahren durch andere (nicht OOP) Programmierung geprägtes Gehirn sich nur schwer darauf einlassen mag. Wenn ich es richtig verstehe, ist bindAsEventListener wohl der Schlüssel, aber ich kapiere es einfach nicht. Und zudem erscheint mir das dort gegebene Beispiel auch falsch zu sein.

                Wobei ich mich Frage was du da vorhast, sieht ja nach einer Anwendung für AJAX aus oder was machst du mit den ganzen iframes?

                Prinzipiell hast du Recht. Es geht darum, Code in die iframes zu laden und daraus einen Teil zu extrahieren, der durch ein Element mit einer eindeutigen id gekennzeichnet ist. Diese brauche ich, um den Teil zu identifizieren, auf den der Benutzer Einfluss nehmen kann. Denn drumherum ist immer eine komplette HTML-Seite, die von einem Server generiert wird. Natürlich bekomme ich die Seite auch mit AJAX geliefert, aber eben nur als Text und dann muss ich mir den interessierenden Teil selbst extrahieren. Das wäre zwar schneller, weil die komplette Verarbeitung des Browsers entfiele, aber auch fehleranfälliger, wenn der Benutzer dort invalide Konstrukte abgelegt hat.

                Um nochmal auf mein fehlendes Verständnis zurückzukommen - gibt es irgendwo eine Einführung (von mir aus auch auf Englisch), die einem nicht-OOP-Programmierer die speziellen Möglichkeiten von Javascript näher bringt? Ich habe das Gefühl, dass ich immer nur an der Oberfläche kratze und mir nur das Aha-Erlebnis fehlt, um tiefer einzudringen.
                Dabei geht es mir nicht um Begrifflichkeit - ich kann durchaus etwas mit Objekten, Methoden, Scope, Closures etc. anfangen. Mit der Vererbung wird es schon schwieriger. Auch die Art der Definition von Objekten mit der Doppelpunkt-Notation (var obj = {name: 'value', fx: function ...};) bereitet mir große Schwierigkeiten, wenn es um deren Auswirkungen außerhalb des jeweiligen Objektes geht.

                Wo ist bloß der Kalklöser ...

                Ralf

                1. Wie kann ich denn Variablen an die anonyme Funktion übergeben? Geht das genauso wie sonst auch? Also:

                  Event.observe(nf, 'load', function(p1,p2) { ...

                    
                  Prinzipiell ja, nur in dem Falle nützt dir das nichts, da die Funktion ja innerhalb des Objektes observe aufgerufen wird mit dort definierten Parametern (müßte in der Doku stehen)  
                    
                  
                  > > Ich würde mir die Api von prototype mal genauer anschauen.  
                  >   
                  > Daran verzweifliche ich gerade, weil mein seit über 30 Jahren durch andere (nicht OOP) Programmierung geprägtes Gehirn sich nur schwer darauf einlassen mag. Wenn ich es richtig verstehe, ist [bindAsEventListener](http://prototypejs.org/api/function/bindAsEventListener) wohl der Schlüssel, aber ich kapiere es einfach nicht. Und zudem erscheint mir das dort gegebene Beispiel auch falsch zu sein.  
                    
                  Wie gesagt da kann ich dir nicht weiterhelfen, ich habe die noch nie benutzt, studiere aber immer mal wieder den Code.  
                    
                  
                  > Um nochmal auf mein fehlendes Verständnis zurückzukommen - gibt es irgendwo eine Einführung (von mir aus auch auf Englisch), die einem nicht-OOP-Programmierer die speziellen Möglichkeiten von Javascript näher bringt?  
                    
                  Schwer zu sagen,  
                  In meinen Bokmark ist z.b. das <http://phrogz.net/JS/Classes/OOPinJS.html>  
                    
                  
                  > Auch die Art der Definition von Objekten mit der Doppelpunkt-Notation (var obj = {name: 'value', fx: function ...};) bereitet mir große Schwierigkeiten, wenn es um deren Auswirkungen außerhalb des jeweiligen Objektes geht.  
                    
                  Dabei ist das trivial. Das das ein Objekt ist hast du ja schon erkannt, der Rest sind einfach die Schlüssel und Werte:  
                  var objekt = {  
                  key1: 'wert1',  
                  key2': 'wert2'  
                  };  
                    
                  Das entspricht exakt:  
                  var objekt = new Object();  
                  objekt.key1 = 'wert1';  
                  objekt.key2 = 'wert2';  
                    
                  Wenn der wert eine Funktionsreferenz ist, kannst du über den Schlüssel die Funktion aufrufen.  
                    
                  objekt.key3 = function() { alert('hallo'); };  
                  objekt.key3();  
                    
                  eigentlich ganz simpel.  
                    
                  Struppi.
                  
                  -- 
                  [Javascript ist toll](http://javascript.jstruebig.de/) (Perl auch!)
                  
                  1. Prinzipiell ja, nur in dem Falle nützt dir das nichts, da die Funktion ja innerhalb des Objektes observe aufgerufen wird mit dort definierten Parametern (müßte in der Doku stehen)

                    Die Funktion erhält den Event als Parameter. Entscheidend ist, in welchem Scope die Funktion läuft (worauf bezieht sich this?)und das hängt vom Browser ab. Für Firefox und Opera ist es das Element, also in diesem Fall der iframe. Für den IE ist es jedoch window, was an der unterschiedlichen Implementation der Events liegt. Und da kommt dann bindAsEventListener ins Spiel, wobei mir die Forensuche geholfen hat: http://forum.de.selfhtml.org/archiv/2007/2/t147171/#m954903.

                    Wie gesagt da kann ich dir nicht weiterhelfen, ich habe die noch nie benutzt, studiere aber immer mal wieder den Code.

                    Der ist hoch interessant, aber für mich noch nicht zugänglich.

                    Schwer zu sagen,
                    In meinen Bokmark ist z.b. das http://phrogz.net/JS/Classes/OOPinJS.html

                    Das werde ich mir mal ansehen, wenn ich Zeit habe. Für den Moment steht die Lösung im Vordergrund, die ich jetzt auch erreicht habe:

                      
                      var vorlagen = document.getElementsByClassName("vorlage");    //prototype.js  
                      if (!vorlagen.length) return;  
                      var nf = new Array();  
                      for (var i=0; i<vorlagen.length; i++) {  
                        var prodid = vorlagen[i].className.match(/\d+/);  
                        if (prodid) {  
                          nf[i] = document.createElement("IFRAME");  
                          nf[i].style.display = "none";  
                          vorlagen[i].appendChild(nf[i]);  
                    //      nf[i].src = "produkt.aspx?produktid="+prodid; *** tut nicht im FF  
                          nf[i].contentWindow.location.href = "produkt.aspx?produktid="+prodid;  
                          Event.observe(nf[i], 'load', function(evt) {      //prototype.js  
                            var v = this.contentWindow.document.getElementById("vorlage");  
                            if (v) this.parentNode.innerHTML += v.innerHTML;  
                    //        if (v) this.parentNode.appendChild(v); *** und das mag der IE nicht  
                            Event.stop(evt);  
                            }.bindAsEventListener(nf[i]));  
                          }  
                        }  
                    
                    

                    Drei Dinge habe ich verändert:

                    1. Die iframe-Elemente stehen nun in einem Array, damit sie voneinander unterschiedlich sind. Bei meiner ersten Variante gab es nur ein Objekt nf.

                    2. Per bindAsEventListener wird die Funktion an das iframe Element gebunden, so dass mit this ein Bezug darauf möglich ist.

                    3. Die iframes werden nicht mehr am body angehängt sondern direkt in die zu füllenden DIV-Container eingesetzt. Dadurch besteht in der Funktion Zugriff auf den Container via parentNode.

                    Diese Lösung funktioniert und damit bin ich zunächst mal zufrieden. Aber irgendwie habe ich das Gefühl, dass man das auch noch viel eleganter - eben mit Objekten und Methoden lösen könnte. Allein schon der Array für die iframes wirkt irgendwie künstlich.

                    eigentlich ganz simpel.

                    Ich habe auch weniger das Problem mit den Definitionen als vielmehr mit den Auswirkungen wie z.B. Vererbung.

                    Ralf

                2. Hallo,

                  Wie kann ich denn Variablen an die anonyme Funktion übergeben?

                  Wenn ich es richtig verstehe, ist bindAsEventListener wohl der Schlüssel, aber ich kapiere es einfach nicht. Und zudem erscheint mir das dort gegebene Beispiel auch falsch zu sein.

                  Mit bindAsEventListener liegst du richtig, auch was die Weitergabe von Daten an die Funktion angeht. Das Beispiel ist tatsächlich fehlerhaft und etwas hochgestochen. Ein einfacheres Beispiel zur Variablen-Übergabe:

                  var obj = { name: "A nice demo" };  
                  function handler (e, arg1, arg2, arg3) {  
                   alert(this.name + "\narg1: " + arg1 + "\narg2: " + arg2 + "\narg3: " + arg3);  
                  }  
                  Event.observe(window, "load", function () {  
                   Event.observe(document.body, "click", handler.bindAsEventListener(obj, 1, 2, 3));  
                  });
                  

                  Mathias