-1

I've been following this youtube video https://www.youtube.com/watch?v=1-CvPn4AbT4 to build a mobile audio player with playlist to embed in a music website. At 1:12:16 I'm getting stuck with an error reading; Uncaught TypeError: Cannot read properties of null (reading 'addEventListener'). I ran it in a js validator and get; Functions declared within loops referencing an outer scoped variable may lead to confusing semantics. (liAudioTag). I've tried changing the name but no go. How can I fix this?

I am by no means an expert in javascript or app building but I'm trying my best to learn. Any help would be appreciated.

const ulTag = wrapper.querySelector("ul");//1:07:10

for (let i = 0; i < allMusic.length; i++) {
    let liTag = `<li>
                    <div class="row">
                        <span>${allMusic[i].name}</span>
                        <p>${allMusic[i].artist}</p>
                    </div>
                    <audio class="${allMusic[i].src}" src="media/${allMusic[i].src}.mp3"></audio>
                    <span id="${allMusic[i].src}" class="audio-duration">3:40</span>
                </li>`;
    ulTag.insertAdjacentHTML("beforeend", liTag);//1:08:28
    
    let liAudioDuaration = ulTag.querySelector(`#${allMusic[i].src}`);//1:10:34
    let liAudioTag = ulTag.querySelector(`.${allMusic[i].src}`);//1:11:29
    
    liAudioTag.addEventListener("loadeddata", ()=>{
        let audioDuration = liAudioTag.duration;    
        let totalMin = Math.floor(audioDuration / 60);
        let totalSec = Math.floor(audioDuration % 60);
        if (totalSec < 10){//add 0 if sec less then 10
          totalSec = `0${totalSec}`;
        }
        liAudioDuaration.innerText = `${totalMin}:${totalSec}`;
    });
    
}
mim
  • 57
  • 9
  • Also; yes I see that liAudioDuaration is spelled wrong but it doesn't seem to matter. – mim Nov 20 '22 at 18:56
  • "*I ran it in a js validator and get; Functions declared within loops referencing an outer scoped variable may lead to confusing semantics.*" - sounds like you put the code in a linter, which won't help you with this error. The warning about functions in loops is irrelevant - it has nothing to do with the error you're getting, and there is no issue from defining the event listener in the loop in your code, since you're using `let` declarations (`const` would be even better). – Bergi Nov 20 '22 at 20:25
  • "*TypeError: Cannot read properties of null (reading 'addEventListener')*" means that `liAudioTag` is `null`, because ``ulTag.querySelector(`.${allMusic[i].src}`)`` returned `null`. Debug what selector you were using. Show us what the value of `allMusic[i].src` is - most likely it's not a valid class attribute value, and not a valid selector either. – Bergi Nov 20 '22 at 20:27
  • thanks for the reply. I wasn't sure if I'd get one. So your response if helpful but as I said I am a novice but trying to learn. I haven't learned how to "Debug" but maybe this is what you asking for? – mim Nov 20 '22 at 20:35
  • ** let allMusic = [ { name: "1. Causes of the Causes", artist: "Jeff Jirout", img: "4_ev_run", src: "Causes of the Causes" }, { name: "3. Forever On The Run", artist: "Jeff", img: "AJWslowroll", src: "Forever On The Run" } ] ** – mim Nov 20 '22 at 20:35
  • Ah yes. `Forever On The Run` is not a good class name - it's multiple classes. And `.Forever On The Run` will not select your element. Do not use arbitrary values in HTML. Use the index `i` as a proper identifier instead, like ` – Bergi Nov 20 '22 at 20:41
  • wow...THANKS SO MUCH. I've been staring at this for days. I took out the spaces in the allMusuc section and now it works like a charm. Your a life saver. Have a great holiday seasson. cheers – mim Nov 20 '22 at 20:47
  • To Bergi, can I give you a badge or something for helping me? – mim Nov 20 '22 at 20:52
  • I changed #duration${i} and #audio${i} as well as audio id instead of class and it works perfectly. Again, thank you Bergi. – mim Nov 20 '22 at 21:32

1 Answers1

1

Using the src of an audio file as a DOM identifier or class name is not a good idea, especially when it can contain spaces. You cannot use it via a selector then. I'd rather recommend to just enumerate them, combining a constant with the loop index:

const ulTag = wrapper.querySelector("ul");//1:07:10

for (let i = 0; i < allMusic.length; i++) {
    const liTag = `<li>
                <div class="row">
                    <span>${allMusic[i].name}</span>
                    <p>${allMusic[i].artist}</p>
                </div>
                <audio id="audio${i}" src="media/${allMusic[i].src}.mp3"></audio>
                <span id="duration${i}" class="audio-duration">3:40</span>
            </li>`;
    ulTag.insertAdjacentHTML("beforeend", liTag)
    
    const liAudioDuaration = ulTag.querySelector(`#duration${i}`);
    const liAudioTag = ulTag.querySelector(`#audio${i}`);
    
    liAudioTag.addEventListener("loadeddata", () => {
        const audioDuration = liAudioTag.duration;    
        const totalMin = Math.floor(audioDuration / 60);
        let totalSec = Math.floor(audioDuration % 60);
        if (totalSec < 10){//add 0 if sec less then 10
          totalSec = `0${totalSec}`;
        }
        liAudioDuaration.innerText = `${totalMin}:${totalSec}`;
    });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375