Anna Bella: Funktioniert Load auch mit Javascript?

Hallo!

Kann ich den "load"-EventListener auch verwenden, wenn ich Bilder mit JAVASCRIPT lade?

<div id="wrapper"></div>
<p id="paragraph">Vogel wird geladen!</p>
var wrapper = document.getElementById("wrapper");
var Rotkehlchen = document.createElement("img");
Rotkehlchen.src = "bilder/voegel/rotkehlchen.jpg";
wrapper.appendChild(Rotkehlchen);

Rotkehlchen.addEventListener("load", function(event) {
    document.getElementById("paragraph").innerHTML = "Rotkehlchen";
});

Danke für eure Hilfe!
Liebe Grüße, Anna.

  1. Hallo Anna,

    mach's sicherheitshalber andersrum: erst den load Listener registrieren, dann src setzen und ins DOM hängen.

    Dann sollte das so funktionieren.

    Rolf

    --
    sumpsi - posui - obstruxi
  2. Hallo Anna,

    dein HTML und JS könnte verbessert werden:

    • <figure> und <figcaption> verwenden, du hast eine Abbildung mit Titel
    • ein <div> ist trotzdem sinnvoll, damit Du die Anordnung von Bild und Titel nicht im JavaScript festlegen musst. Eventuell ein <span> statt eines <div> - je nachdem, ob Du ein Inline- oder ein Blockelement als Container für's Bild brauchst.
    • Die id kommt dann an die figure
    • Das Suchen der Elemente ist dann mit getElementById umständlich, querySelector ist bequemer
    • Je nach Aufbau deiner Seite musst Du den Bildcontainer vor dem appendChild löschen. Das geht am einfachsten mit innerHTML="".
    • Den Text "Rotkehlchen" solltest Du aber mittels textContent setzen. Du setzt kein HTML, darum braucht man dafür auch nicht den HTML Parser anzuwerfen.
    • Falls dein JavaScript NICHT Teil einer Funktion ist, sondern frei in einem <script> Element steht, solltest Du es kapseln, damit deine Variablen nicht als "Datenmüll" stehen bleiben. Dazu verwendet man einen Funktionsausdruck.
    <figure id="vogelbild">
    <div></div>
    <figcaption>Vogel wird geladen!</p>
    </figure>
    
    let vogelbild = document.querySelector("#vogelbild div");
    let titel = document.querySelector("#vogelbild figcaption");
    let rotkehlchen = document.createElement("img");
    
    rotkehlchen.addEventListener("load", function(event) {
        titel.textContent = "Rotkehlchen";
    });
    
    rotkehlchen.src = "bilder/voegel/rotkehlchen.jpg";
    vogelbild.innerHTML = "";    // alten Inhalt löschen
    vogelbild.appendChild(rotkehlchen, titel);
    

    Wie macht man einen Funktionsausdruck? Ich baue es schrittweise auf. Du erzeugst eine anonyme Funktion, in der Dein Code steht. Und die rufst Du dann auf. Mit einer temporären Variablen würde das so aussehen:

    let dummyFunction = function() {
       // dein Code hier
    };
    dummyFunction();
    

    Der Trick ist jetzt, die temporäre Variable loszuwerden. Du weißt sicherlich, dass man in JavaScript überall, wo ein Wert erwartet wird, auch einen Ausdruck hinschreiben kann, der diesen Wert ermittelt. Da Funktionen in JavaScript ganz normale Objekte sind, gibt es auch Ausdrücke, deren Wert eine Funktion ist.

    Aber ganz so einfach geht's auch nicht, das hat historische Gründe. JavaScript interpretiert das als die klassische Syntax zum Definieren einer Funktion und fällt dann beim abschließenden Aufruf mit () auf die Nase.

    function() {
       // dein Code hier
    }();
    // Uncaught SyntaxError: Function statements require a function name
    

    Klammern zu Hilfe!

    (function() {
       // dein Code hier
    })();
    

    Es sind absolut verrückt aus, aber es funktioniert. Du erzeugst eine freifliegende Funktion, rufst sie im Fluge auf und lässt sie dann in den Abgrund des Garbage Collectors fallen 😉.

    Äh nein - lässt Du nicht wirklich. Weil: Du registrierst ja einen Event Listener. D.h. die innere Funktion des Listeners wird vom DOM referenziert, und der Garbage Collector stellt fest: wird noch gebraucht. Und weil eine Funktion auch immer den Kontext referenziert, in dem sie erzeugt wurde, hat auch deine anonyme Funktion noch jemanden, der sie festhält. Sowas kann bei übermäßigem Einsatz Speicherlecks hervorrufen. Es wäre daher hilfreich, den Eventlistener nach Aufruf zu deregistrieren. Dafür muss man der Eventhandlerfunktion noch einen Arbeitsnamen geben.

    (function() {
       let vogelbild = document.querySelector("#vogelbild div");
       let titel = document.querySelector("#vogelbild figcaption");
       let rotkehlchen = document.createElement("img");
    
       rotkehlchen.addEventListener("load", function handler(event) {
           titel.textContent = "Rotkehlchen";
           rotkehlchen.removeEventListener("load", handler);
       });
    
    rotkehlchen.src = "bilder/voegel/rotkehlchen.jpg";
    vogelbild.innerHTML = "";    // alten Inhalt löschen
    vogelbild.appendChild(rotkehlchen, titel);
    })();
    

    So. Nun ist's sauber 😀.

    Es gibt auch eine Variante von addEventListener, die den Handler nach einmaligem Aufruf automatisch deregistriert, aber die funktioniert nicht im Internet Explorer.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf

      (function() {
         let vogelbild = document.querySelector("#vogelbild div");
      
         ...
      
      })();
      

      Wenn du let (oder besser const) verwendest, brauchst du keinen IIFE, ein einfaches Block Statement genügt. Nur Variablen die mit var deklariert werden sind an einen Ausführungskontext gebunden.

      {
          const vogelbild = document.querySelector('#vogelbild div');
      
          ...
      
      }
      

      Die Variable ist nur innerhalb des Blocks sichtbar. Nach der schließenden geschweiften Klammer wird die lokale Umgebung des Blocks samt Bindung für die Variable zerstört.

      Viele Grüßé

      1. Hallo Shadow,

        oh. Stimmt. Haben IIFEs dann überhaupt noch einen Zweck oder sind sie ein Relikt aus der Zeit vor let?

        Rolf

        --
        sumpsi - posui - obstruxi
    2. Ok, aber was ist wenn ich jetzt viele Vogelbilder habe? Brauche ich dann für jedes Vogelbild einen Eventlistener oder kann ich dann in dem Funktionsausdruck auch document.addEventListener("load") sagen?

      1. Hallo Anna,

        viel Geflügel bedeutet vermutlich auch viele Daten - und an dem Punkt solltest Du überlegen, ob das, was Du da tust, richtig ist. Wieviele MB verbraucht deine Seite beim Aufruf? Wie lange dauert das insgesamt? Die Entwicklertools des Browsers geben Auskunft, auf der Netzwerk-Seite.

        Denk auch immer an den armen Handybenutzer, der gerade in einer Prä-LTE Zone herumschneckt, und der ggf. einen Mobilfunkvertrag mit 300MB pro Monat hat. Eine Seite mit Thumbnail-Bildern und ggf. etwas JavaScript für eine Galerie wirken da Wunder.

        Aber bleiben wir bei deinem Anliegen. Leider musst Du für jedes Bild einen eigenen load Handler registrieren. Denn das load Event blubbert nicht, deshalb kannst Du es nicht mit einem zentralen Handler lösen.

        Du musst aber nicht eine eigene Eventhandlerfunktion für jedes Bild schreiben, da gibt es bessere Möglichkeiten. Um dafür einen Vorschlag machen zu können, müsste man etwas mehr über deine Seite wissen. Schreibst Du die ganze Seite als HTML von Hand? Generierst Du sie über ein Script? Magst Du die URL dafür bekanntgeben?

        Rolf

        --
        sumpsi - posui - obstruxi
    3. @@Rolf B

      (function() {
         let vogelbild = document.querySelector("#vogelbild div");
         let titel = document.querySelector("#vogelbild figcaption");
         let rotkehlchen = document.createElement("img");
      
         rotkehlchen.addEventListener("load", function handler(event) {
             titel.textContent = "Rotkehlchen";
             rotkehlchen.removeEventListener("load", handler);
         });
      
      rotkehlchen.src = "bilder/voegel/rotkehlchen.jpg";
      vogelbild.innerHTML = "";    // alten Inhalt löschen
      vogelbild.appendChild(rotkehlchen, titel);
      })();
      

      So. Nun ist's sauber 😀.

      Nein! Ein img-Element ohne alt-Attribut ist nicht ganz sauber. So ein Anfängerfehler sollte dir nicht passieren.

      Da fehlt zumindest

      rotkehlchen.alt = '';
      

      Leerstring, denn ein Screenreader soll „Rotkehlchen“ (was schon die Bildunterschrift ist) ja nicht doppelt vorlesen.

      Noch besser: die Bildunterschrift auch gleich als accessible label nehmen. Dazu braucht diese eine ID:

      <figcaption id="vogelbildunterschrift">
      

      Und dann:

      rotkehlchen.setAttribute('aria-labeledby', 'vogelbildunterschrift');
      

      🖖 Stay hard! Stay hungry! Stay alive! Stay home!

      --
      Home Office ist so frustierend, weil man jetzt noch viel stärker bemerkt mit wievielen Menschen man zu tun hat, die nicht sinnerfassend lesen können. (@Grantscheam)
      1. Hallo Gunnar,

        ich akzeptiere deine gute Absicht, aber deinen Ton weise ich zurück. Der verletzt. Sicherlich ist nicht mein ganzer Code rot zu machen, nur weil das alt-Attribut nicht gesetzt ist.

        Ich spiegele dich mal: <ironie> Dein Beitrag ist kompletter Müll. Das Attribut aria-labeledby gibt es nicht. Es heißt aria-labelledby, auch wenn laut Google ca. 37.500 Fliegen auf diesem Misthaufen sitzen. </ironie>

        Aber trotzdem danke für den Stupser. Ich habe das alt-Attribut natürlich komplett vergessen, weil ich nicht täglich Webseiten baue, und die nun folgende Recherche zur figcaption folgte auf Grund meines Ärgers über deine Art der Kritik.

        Ich habe nochmal in der HTML 5.2 Spec nachgelesen, da ist eine Aufstellung von Fällen bezüglich der Inhalte von src und alt.

        src ist gesetzt, alt ist Leerstring: Bild ist dekorativ oder eine (redundante) Ergänzung zu anderen Infos im Dokument

        src ist gesetzt, alt ist nicht leer: Bild ist Inhalt, alt enthält ein Text-Äquivalent oder -Ersatz für das Bild

        src ist gesetzt, alt aber nicht: In dem Fall wird recht ausführlich und verquast dem Browser angeraten, sich an der figcaption zu orientieren. Und in der MDN steht, dass eine figure implizit die Rolle aria-labelledby zur figcaption enthält.

        Das manuelle Setzen von aria-labelledby sollte also unnötig sein, und da Anna nach vielen figures fragt, ist das auch gut so, denn dann würde man eine Menge IDs auseinander halten müssen.

        Du schlägst ein leeres alt-Attribut vor. Laut Spec bezeichnet das eine reine Deko. Ich glaube, das ist hier nicht so. Hier kommt Fall 3 in Frage, mit dem die Spec ein fehlendes alt-Attribut in einer figure mit figcaption als valide vorsieht. Leider sagt die Spec für Fall 3 nicht, ob das Bild dann als Inhalt oder als Dekoration anzusehen ist.

        Es gibt aber noch einen anderen Satz:

        In the case where an img without an alt attribute is the child of a figure element with a non-empty figcaption element, the image’s presence should be minimally conveyed to a user by Assistive Technology, typically by identifying the image role.

        D.h. man müsste dem Bild eine role geben. Aber welche? role=figure und role=img gibt es - aber die sollten ein figure- und ein img-Element doch by default mitbringen. Dazu hätte ich gerne deine Meinung gelesen.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. @@Rolf B

          ich akzeptiere deine gute Absicht, aber deinen Ton weise ich zurück.

          ??

          Sicherlich ist nicht mein ganzer Code rot zu machen, nur weil das alt-Attribut nicht gesetzt ist.

          Ach so, du meinst den Farbton. Der Markdown-Parser gibt es AFAIS nicht her, Zeilen oder Abschnitte in Codeblöcken farblich zu markieren.

          Ich spiegele dich mal: <ironie> Dein Beitrag ist kompletter Müll.

          Du hast einen Zerrspiegel.

          Das Attribut aria-labeledby gibt es nicht. Es heißt aria-labelledby, auch wenn laut Google ca. 37.500 Fliegen auf diesem Misthaufen sitzen. </ironie>

          Ich schlag jedes Mal nach, wie das geschrieben wird. Diesmal bin ich wohl an die falsche Stelle geraten. 😠

          Ist aber auch verwirrend, das labelled falsch™ geschrieben wird.

          AE BE
          color colour
          labeled labelled

          Man sollte in einer Spalte bleiben, nicht in einer Diagonalen.

          🖖 Stay hard! Stay hungry! Stay alive! Stay home!

          --
          Home Office ist so frustierend, weil man jetzt noch viel stärker bemerkt mit wievielen Menschen man zu tun hat, die nicht sinnerfassend lesen können. (@Grantscheam)
          1. Hallo,

            Ich spiegele dich mal: <ironie> Dein Beitrag ist kompletter Müll.

            Du hast einen Zerrspiegel.

            du hast das ironie-Tag übersehen?

            aria-labelledby

            Ich schlag jedes Mal nach, wie das geschrieben wird. Diesmal bin ich wohl an die falsche Stelle geraten. 😠

            Ist aber auch verwirrend, das labelled falsch™ geschrieben wird.

            Ja, das ist mir auch schon aufgefallen. Dachte heute morgen schon: Hey, solche Spezifikationen und Festlegungen kommen doch meist aus Amerika, nicht von den Briten.

            AE BE
            color colour
            labeled labelled

            Ganz entsprechend: traveled, canceled ... and many more.
            Andererseits wird in CSS ja auch grey als Farbe akzeptiert.

            Live long and pros healthy,
             Martin

            --
            Ich stamme aus Ironien, einem Land am sarkastischen Ozean.