Linuchs: Variablen angeblich in function nicht bekannt

Moin,

ich hatte das Muster einer Audio-Playlist versprochen.

var obj_audio;  // das Audio-Element
var arr_li;     // Array der li Elemente (Titel)
var lfd_nr;     // nr des ausgewaehlten li - Elements

function playTitel( nr ) {  // li - Nummer
  // playlist auf Anfang setzen
  if ( nr > arr_li.length ) nr  = 1;
  lfd_nr  = nr;

  // Titel und Kuenstler anzeigen
  document.getElementById( "lied_titel" ).innerHTML = arr_li[nr].innerHTML +"   " +arr_li[nr].getAttribute("data-gruppe");

  // source in den Player
  obj_audio.src           = arr_li[nr].getAttribute("data-src"); // Zeile 71

  // Player starten
  document.getElementById( "player" ).play();
}

Die drei Variablen sind noch nicht gefüllt. Kein Problem, solange playTitel nicht aufgerufen wird.

Nun ergänze ich den Code um


// Array der li Elemente
arr_li    = document.querySelectorAll("li");

// click event fuer jedes li Element
for ( let i=0; i<arr_li.length; i++ ) {
  arr_li[i].addEventListener('click', playTitel ( i ) );  
}

// das Audio-Objekt
obj_audio = document.getElementsByTagName("audio")[0];

// naechsten Titel spielen
obj_audio.addEventListener('ended', playTitel( lfd_nr +1 ) );

und bekomme den Fehler Uncaught TypeError: obj_audio is undefined, bezogen auf Zeile 71 in playTitel.

Wenn ich die addEventListener auskommentiere, kommt der Fehler nicht.

Habe auch die Reihenfolge der Zuordnungen vertauscht, ändert nichts am Fehler.

Was mache ich falsch?

Gruß, Linuchs

  1. Irgendwas habe ich grundsätzlich falsch verstanden.

    Die Zeile

    arr_li[i].addEventListener('click', playTitel ( i ) );
    

    durchläuft die function playTitel sofort, ganz ohne click.

    Ich arbeite dran ...

    1. Hi there,

      Irgendwas habe ich grundsätzlich falsch verstanden.

      ja

      Die Zeile

      arr_li[i].addEventListener('click', playTitel ( i ) );
      

      durchläuft die function playTitel sofort, ganz ohne click.

      Wenn Du die Funktion so angibst, wird sie immer sofort ausgeführt. Richtiger wäre:

      arr_li[i].addEventListener('click', function (){
      
      playtitel(i);
      
      } );
      

      wobei das "i" an dieser Stelle problematisch werden kann, weil es je nachdem, für welchen Bereich es gilt, uU nicht gefunden wird/definiert ist...

  2. Hallo Linuchs,

    Was mache ich falsch?

    Eine Menge. Zunächst ein Tipp, der nicht direkt in Bezug zur Meldung steht.

    arr_li[nr].getAttribute("data-gruppe")
    arr_li[nr].getAttribute("data-src")
    
    arr_li[nr].dataset.gruppe
    arr_li[nr].dataset.src
    

    Data-Attribute spricht man über die dataset Eigenschaft an. Der einzige Browser, der dataset nicht kennt, ist der Internet Explorer vor Version 11, und das ist komplett irrelevant.

    Zur eigentlichen Frage. Dies ist definitiv falsch:

    obj_audio.addEventListener('ended', playTitel( lfd_nr +1 ) );
    

    So, wie ich das sehe, ist playTitel der Eventhandler, und nicht eine Funktion, die einen Eventhandler zurückgibt. Deswegen darfst Du an der Stelle, wo der Eventhandler erwartet wird, nicht einfach die Funktion aufrufen. Das würde nur einmal passieren, bei der Registrierung. Aber du möchtest, dass es beim Auslösen des Events passiert.

    Ich nehme an, du möchtest bei Eintreten eines ended Events die lfd_nr um 1 erhöhen und sie, wenn sie zu groß wird, auf den Anfang zurücksetzen. Und dann soll der Audiotrack mit der aktuellen lfd_nr gestartet werden.

    Problem 1: Array-Indexe sind 0-basierend, aber deine lfd_nr ist 1-basierend. Korrekt wäre, lfd_nr auf 0 zu initialisieren, bei Überlauf auf 0 zurückzusetzen und zur Abfrage des Überlaufs lfd_nr >= arr_li.length zu testen.

    Da lfd_nr ohnehin eine globale Variable ist, brauchst Du die auch nicht als Parameter für playTitel. Mach's einfach global.

    Zum Start des ersten Liedes wirst Du playTitel eigenständig aufrufen müssen, nicht als Eventhandler, deswegen ist es sinnvoll, wenn playTitel einfach nur das Lied mit der Nummer N startet und sich nicht um die Umlaufsteuerung kümmert. Dafür mach eine zweite Funktion, und lass die den Eventhandler sein.

    Das Audio-Element ermittelst Du eleganter über querySelector. Dumme Frage: Ist das Element mit der id player und das audio-Element identisch? Dementsprechend die Frage unten im Code.

    Das ganze hab ich noch in eine Funktion gesteckt, damit die globalen Variablen nicht zu global sein müssen. Die darfst Du allerdings nur genau einmal aufrufen, sonst hast Du zwei Eventhandler. Wenn Du sowas wie stopAudioLoop oder resetAudioLoop willst, musst Du das anders strukturieren (z.B. mit Hilfe des revealing module pattern.

    function startAudioLoop()
    {
      // das Audio-Element
      const obj_audio = document.querySelector("audio");
      // Array der li Elemente (Titel)
      const arr_li = document.querySelectorAll("li");
      // Ausgabeelement für Liedtitel
      const lied_titel = document.getElementById( "lied_titel" );
      // nr des ausgewaehlten li - Elements
      let   lfd_nr = 0;
      
      function playTitel(nr) {
        // Titel und Kuenstler anzeigen
        lied_titel.innerHTML = arr_li[nr].innerHTML +
                               " &nbsp; " +
                               arr_li[nr].dataset.gruppe;
        // source in den Player
        obj_audio.src        = arr_li[nr].dataset.src
        document.getElementById( "player" ).play();
        // Frage: warum nicht
        obj_audio.play();
      }
    
      obj_audio.addEventListener("ended", function() {
         lfd_nr = lfd_nr + 1;
         if (lfd_nr >= arr_li.length)
            lfd_nr = 0;
         playTitel(lfd_nr);
      });
    
      playAudio(0);
    }
    

    Das ist jetzt ungetestet ins Forum gecoded und geht möglicherweise irgendwo kaputt. Good Luck.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Zunächst ein Tipp, der nicht direkt in Bezug zur Meldung steht.

      Tja, wie unlogisch ist das denn, das HTML-Attribut heißt data-gruppe und JS konnte es nicht mit data.gruppe finden. Also recherchieren und dann kommt sowas dabei raus ...

      Okay, habs geändert auf dataset

      Wie klawischnigg schreibt, war das click-Event vollkommen falsch aufgebaut. Habe ich geändert auf

      // click event fuer jedes li Element
      for ( let i=0; i<arr_li.length; i++ ) {
        arr_li[i].addEventListener('click', function() {
          playTitel( i );
        });
      }
      // naechsten Titel spielen
      obj_audio.addEventListener('ended', function() {
        playTitel( lfd_nr*1 +1 );
      });
      

      So, vier Titel laufen wunderbar in Dauerschleife. Die nächsten Tage dokumentiere ich das, das reicht für Hörproben auf einer Webseite.

      Dann möchte ich erweitern auf zufällige Reihenfolge und schließlich die Titel variabel an das HTML-Dokument übergeben. Mein Favorit wäre da ja ein CSV-String, der von PHP aus der Medien-Datenbank generiert wird.

      So weit ich weiß, kann JS wohl Dateinamen eines lokalen Ordners nicht ermitteln, oder gibt's da einen Trick?

      Gruß Linuchs

  3. Hast Du eigentlich meine Antwort auf Deine frühere Frage gelesen? Hat sie geholfen?

    https://forum.selfhtml.org/self/2022/feb/15/unerklarter-js-fehler-beim-start-mehrerer-audio-spuren/1796342#m1796342

    1. Danke der Nachfrage, habe ich tatsächlich nicht gesehen, muss ich mich noch drum kümmern, bin jetzt am Projekt Playlist zur Veröffentlichung bei SelfHTML.