ezzemm: JS/DOM - Bild vorladen innerhalb einer Funktion

Hallo zusammen!

Ich habe vor kurzen angefangen Javascript zu lernen und habe u.a. auch durch Suche hier im Board schon viele Probleme lösen können. Doch aktuell komme ich nicht weiter und ich hoffe ihr könnt mir helfen.

Ich habe ein kleines Lightbox-Skript geschrieben, das soweit auch schon funktioniert. "Soweit" bedeutet, daß es funktioniert wenn das entsprechende Bild schon im Cache liegt. Und das Bild in den Cache zu bekommen, darin liegt mein Problem.

Im Netz findet man viele Lösungsvorschläge; z.B. jenen den ich hier poste:
-------------------------------------------------------------------

  var newImage = new Image();  
     newImage.onload = function () { imageIsLoaded( this ) };  
    newImage.src = imgPath + imgNames[i];

-------------------------------------------------------------------

Da habe ich schon ein Verständnisproblem. Wie kann in der einen Funktion ein Bild erzeugt und gecacht werden, wo erst in der nächsten Funktion der Dateipfad angegeben wird? Und kann man das nicht innerhalb einer Funktion abhandeln?

Ich poste hier mal meinen kompletten Code; ich hoffe einer von euch kann mir bei meinem Verständnisproblem auf die Sprünge helfen :)

HTML-Code:
-------------------------------------------------------------------
<a name="bildname.jpg" href="#" onclick="LightBox(this)">Linkname</a>
-------------------------------------------------------------------

JS-Code:
-------------------------------------------------------------------

function LightBox(param) {  
 var opacity = 80; // Transparenz des Hintergrunds  
 var gap = 20; // Mindestbreite des transparenten Rahmens um das Bild (in px)  
  
 var img = new Image();  
  img.src = param.name;  
  
 var ratio = img.height / img.width;  
 var imgX, imgY, resize;  
 if (img.width + gap >= document.body.clientWidth) { // Skalierung falls Bild zu breit  
  imgX = document.body.clientWidth - gap;  
  imgY = imgX * ratio;  
  img.style.width = imgX;  
  img.style.height = imgY;  
  resize = true;  
 } else if (img.height + gap >= document.body.clientHeight) { // Skalierung falls Bild zu hoch  
  imgY = document.body.clientHeight - gap;  
  imgX = imgY / ratio;  
  img.style.width = imgX;  
  img.style.height = imgY;  
  resize = true;  
 } else {  
  imgX = img.width;  
  imgY = img.height;  
  img.style.width = imgX;  
  img.style.height = imgY;  
  resize = false;  
 }  
  
 var body = document.getElementsByTagName("body");  
 var backLayer = document.createElement("div"); // <div>-Layer: Transparenter Hintergrund  
   backLayer.style.position = "absolute";  
  backLayer.style.top = "0px";  
  backLayer.style.left = "0px";  
  backLayer.style.width = document.body.clientWidth+"px";  
  backLayer.style.height = document.body.clientHeight+"px";  
  backLayer.style.backgroundColor = "black";  
  if ((document.all) && (document.getElementById)) { // Kontrolle auf IE6  
   backLayer.style.filter = "alpha(opacity=" + opacity + ")";  
  } else if ((!document.all) && (document.getElementById)) { // Kontrolle auf Firefox  
   backLayer.style.opacity = opacity/100;  
  }  
 var frontLayer = document.createElement("div"); // <div>-Layer: zentrierter Vordergrund  
  frontLayer.style.position = "absolute";  
  frontLayer.style.left = (document.body.clientWidth - imgX) / 2;  
  frontLayer.style.top = (document.body.clientHeight - imgY) / 2;  
  frontLayer.style.cursor = "pointer";  
 var a = document.createElement("a"); // Link der das Bild wieder schließt...  
  if (resize) {  
  a.onclick = function close() { // ... wenn es skaliert wurde  
    body[0].removeChild(body[0].lastChild);  
    body[0].removeChild(body[0].lastChild);  
    document.location.href = param.name;  
   }  
  } else {  
   a.onclick = function close() { // ... wenn es nicht skaliert wurde  
    body[0].removeChild(body[0].lastChild);  
    body[0].removeChild(body[0].lastChild);  
   }  
  }  
 a.appendChild(img);  
 frontLayer.appendChild(a);  
 body[0].appendChild(backLayer);  
 body[0].appendChild(frontLayer);  
}

-------------------------------------------------------------------

  1. Liebe(r) ezzemm,

    hier im Board

    das war ein böser Fehler! Viele hier sind stolz darauf, dass das hier eben kein Board, sondern tatsächlich ein Forum ist (im Gegensatz zu vielen Foren, die genauer betrachtet eben doch Boards sind)... ;-)


    var newImage = new Image();

    newImage.onload = function () { imageIsLoaded( this ) };
        newImage.src = imgPath + imgNames[i];

    
    > -------------------------------------------------------------------  
      
    Soweit klar.  
      
    
    > Da habe ich schon ein Verständnisproblem. Wie kann in der einen Funktion ein Bild erzeugt und gecacht werden, wo erst in der nächsten Funktion der Dateipfad angegeben wird? Und kann man das nicht innerhalb einer Funktion abhandeln?  
      
    Hier wird garnix "in einer Funktion gecached". Du musst Dir das völlig anders vorstellen.  
      
    1\.) Du erzeugst ein neues Bild-Objekt. Klar.  
    2\.) Du weist dem Bildobjekt eine Datenquelle zu. Klar.  
    3\.) Der Browser fängt (im Hintergrund!!!) mit dem Laden der Bilddaten an.  
    4\.) Du gibst dem Browser eine Anweisung (in Form einer Funktion bzw. eines Funktionsobjektes, das eine andere Funktion namens "imageIsLoaded" aufrufen wird), was er tun soll, wenn er denn mit dem Laden fertig ist. Wie lange das dauern wird, kannst Du (und Dein Script) nicht wissen. Deine Funktion wird zu diesem Zeitpunkt längst abgearbeitet sein. Aber das Funktionsobjekt sagt ihm dann, was er zu tun hat.  
      
    
    > Ich poste hier mal meinen kompletten Code;  
      
    NEIIIIIN!!! Bitte nicht! Bitte nur den Teil, bei dem Du nicht weiterkommst! Insbesondere solltest Du relevante Fehlermeldungen oder unerwartete Scriptaktionen aufführen, zu denen Du die passende Stelle im Script zitierst. In diesem Code-Monster will doch niemand freiwillig nach Pilzen suchen gehen.  
      
    Übrigens hat sich diese Selbstdisziplin bei mir schon oft bezahlt gemacht, denn um die relevante Code-Stelle für einen Forumseintrag zu ermitteln, musste ich mich schon oft so intensiv mit meinem Script auseinandersetzen, dass ich am Ende die Ursache selbst gefunden habe, und damit mein Problem selbst lösen konnte. Aber das nur als Anekdote am Rande.  
      
    Liebe Grüße,  
      
    Felix Riesterer.
    
    -- 
    ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
    
  2. Hallo,

    var newImage = new Image();

    newImage.onload = function () { imageIsLoaded( this ) };
        newImage.src = imgPath + imgNames[i];

    
    > -------------------------------------------------------------------  
    >   
    > Da habe ich schon ein Verständnisproblem. Wie kann in der einen Funktion ein Bild erzeugt und gecacht werden, wo erst in der nächsten Funktion der Dateipfad angegeben wird? Und kann man das nicht innerhalb einer Funktion abhandeln?  
      
    Willkommen in der Welt des Event-Handlings. Solche Sachen werden in JavaScript asynchron und ereignisbasiert gehandhabt. Wenn du src zuweist, dann fängt der Browser zwar an, die Grafik herunterzuladen, aber wie gesagt im Hintergrund. Das Script wird nicht angehalten, sondern läuft weiter. Damit das Script reagieren kann, wenn die Grafik fertig geladen ist, gibt es das load-Ereignis. Und hier wird einfach eine Ereignisüberwachung gestartet, indem man eine Handlerfunktion für das Ereignis "load" zuweist.  
      
    
    >  var img = new Image();  
    >   img.src = param.name;  
    >   
    >  var ratio = img.height / img.width;  
      
    Wie gesagt, du kannst auf die Eigenschaften height und width erst zugreifen, wenn das Bild fertig geladen ist. Das ist es zu dem Zeitpunkt, zu dem diese Codezeile ausgeführt wird, nicht der Fall. Daher musst du ein ereignisgesteuertes Programm schreiben.  
    Packe den restlichen Code der Funktion in eine verschachtelte Funktion und weise die als load-Handler zu:  
      
    img.onload = openLightBox;  
    function openLightBox () {  
       ... restlicher Code ...  
    }  
      
    Durch die Verschachtelung hat die interne Funktion Zugriff auf die Variablen param und img, daher musst du am Code nichts ändern.  
      
    
    >  var body = document.getElementsByTagName("body");  
      
    document.body reicht aus, getElementsByTagName ist unötig.  
    Wenn du schon einen Shortcut nutzt, dann nutze ihn doch konsequent bei allen Vorkommnissen von document.body.  
      
    
    >   if ((document.all) && (document.getElementById)) { // Kontrolle auf IE6  
    >    backLayer.style.filter = "alpha(opacity=" + opacity + ")";  
    >   } else if ((!document.all) && (document.getElementById)) { // Kontrolle auf Firefox  
    >    backLayer.style.opacity = opacity/100;  
    >   }  
      
    Diese Browserweiche ist so unzuverlässig, wie sie unnötig ist. Besser du schreibst eine richtige Fähigkeitenabfrage:  
      
    backLayer.style.opacity = opacity/100;  
    if (typeof backLayer.style.filter != "undefined") {  
       backLayer.style.filter = "alpha(opacity=" + opacity + ")";  
    }  
      
    Oder:  
      
    if (backLayer.[filters](http://msdn.microsoft.com/en-us/library/ms537452(VS.85).aspx)) { ... IE-Fix ... }  
      
    
    >   if (resize) {  
    >   a.onclick = function close() { // ... wenn es skaliert wurde  
    >     body[0].removeChild(body[0].lastChild);  
    >     body[0].removeChild(body[0].lastChild);  
    >     document.location.href = param.name;  
    >    }  
    >   } else {  
    >    a.onclick = function close() { // ... wenn es nicht skaliert wurde  
    >     body[0].removeChild(body[0].lastChild);  
    >     body[0].removeChild(body[0].lastChild);  
    >    }  
    >   }  
      
    Du brauchst die Funktion nicht zweimal notieren. Die Variable resize ist (genauso wie body) darin verfügbar und du kannst die if-Abfrage auch einfach in die Funktion schreiben.  
      
    Mathias
    
    -- 
    [SELFHTML aktuell Weblog](http://aktuell.de.selfhtml.org/weblog/)
    
    1. Lieber molily,

      Diese Browserweiche ist so unzuverlässig, wie sie unnötig ist. Besser du schreibst eine richtige Fähigkeitenabfrage:

      backLayer.style.opacity = opacity/100;
      if (typeof backLayer.style.filter != "undefined") {
         backLayer.style.filter = "alpha(opacity=" + opacity + ")";
      }

      Oder:

      if (backLayer.filters) { ... IE-Fix ... }

      na, na, na, und ich musste mir anhören (lesen), dass das überhaupt unnötig ist. Man weist dem style-Objekt die filter-Eigenschaft einfach zu - nur der IE weiß damit was anzufangen, die restlichen Browser ignorieren das. Fehler kann es keine geben, da die style-Eigenschaften wie in anderen Objekten auch frei definierbar sind. Ob es zu visuellen Effekten kommt, hängt davon ab, ob meine zugewiesene style-Eigenschaft vom Browser visuell umgesetzt und interpretiert wird, oder ob sie (weil sie in CSS keinen Sinn hat) einach nur ein netter Witz ist.

      Liebe Grüße,

      Felix Riesterer.

      --
      ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
      1. Hallo,

        na, na, na, und ich musste mir anhören (lesen), dass das überhaupt unnötig ist.

        Mir ging das fertige Posting verloren, deshalb habe ich beim zweiten Mal nur den rudimentären Text von neuem getippt, da fehlte ein entsprechender Hinweis.

        die restlichen Browser ignorieren das.

        Ja, wobei Firefox eine Warnung in die Konsole schreibt. Wenn man das verhindern will, dann mit einer solchen Fähigkeitenabfrage.

        Mathias

        1. Hallo Felix, hallo molily!

          Vielen Dank für eure Hilfe. Ihr habt mir sehr geholfen; in Hinsicht auf das Script und in Hinsicht auf mein Verständnis von Javascript allgemein. Jetzt weiß ich wieso mein erster Versuch mit img.complete und einer while-Schleife fehlgeschlagen ist ;) Dankeschön :)

          Ein Problem hatte ich dann noch, und zwar funktioniert der Funktions-Aufruf im IE6 nach dem Cachen des Bildes nur, wenn das Bild noch nicht im Cache lag. Wenn es schon mal geöffnet wurde, passiert im IE6 nach Klick auf den Link mit diesem Code nichts:

          img.onload = openLightBox;  
          function openLightBox () {  
             ... restlicher Code ...  
          }
          

          Ich habe das dann mit einer Abfrage nach der img.width abgefangen:

           var img = new Image(); // Erzeugen des Bildes  
            img.src = "bildname.jpg";  
            if (img.width == 0) { // Abfrage ob Bild im Cache  
             img.onload = openLightBox;  
            } else {  
             openLightBox();  
            }  
           function openLightBox() {  
            ... restlicher Code ...  
           }
          
          1. Liebe(r) ezzemm,

            der IE feuert das onload-Event nicht, wenn das Bild bereits im Cache vorhanden ist. Daher muss man - wie Du es von selbst herausgefunden hast - eine Prüfung auf bereits vorhandene width- bzw. height-Werte vornehmen, um bei bereits vorhandenen Werten größer null auf das Eventhandling zu verzichten und "gleich weiter zu machen".

            Liebe Grüße,

            Felix Riesterer.

            --
            ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
            1. Hallo,

              Daher muss man - wie Du es von selbst herausgefunden hast - eine Prüfung auf bereits vorhandene width- bzw. height-Werte vornehmen

              Hilft da vielleicht http://de.selfhtml.org/javascript/objekte/images.htm#complete@title=complete weiter? Dann brauch man keine Rückschlüsse von width/height auf den Ladezustand ziehen.

              Mathias