Home > Back-end >  Duration not applying to all rows
Duration not applying to all rows

Time:06-29

I created a music list similar to spotify dynamically is javascript and I am trying to add duration in all rows but the problem is duration is applying to first row only. And console not showing any error. i tried but couldn't figure what's the problem.

// Creating Music List
const tbody = document.querySelector("tbody");
// let create tr tags according to array length for list
for (let i = 0; i < songs.length; i  ) {
    //let's pass the song details from the array
    let trTag = `<tr tr-index = ${i}>
    <td  width="5%">${songs[i].nmbr}</td>
    <td  width="80%">
        <div>
            <img  src=${songs[i].coverPath} >
        </div>
        <div >
            <h3>${songs[i].trackName}</h3>
            <h4>${songs[i].trackArtist}</h4>
            
        </div>
    </td>
    <td width="30%">
        <h4>${songs[i].trackAlbum}</h4>
    </td>
    <td width="15%">${songs[i].date}</td>
    <td width="5%">
        <img  src="/assets/asset 72.svg" alt="">
    </td>
    <td width="12%" >0:00</td>
    </tr> `;


    tbody.insertAdjacentHTML("beforeend", trTag); //inserting the tr inside tbody tag

    let trAudioDuration = tbody.querySelector(".audio_duration");
    let trAudioTag = new Audio(`audio/1.mp3`);

    trAudioTag.addEventListener("loadeddata", () => {
        let aDuration = trAudioTag.duration;
        let totalMin = parseInt(aDuration / 60);
        let totalSec = parseInt(aDuration % 60);
        if (totalSec < 10) {
            totalSec = `0${totalSec}`;
        };
        trAudioDuration.innerHTML = `${totalMin}:${totalSec}`; //passing total duration of song
    });
};

enter image description here

CodePudding user response:

Ahh, this is a classic one! The problem lies with this:

let trAudioDuration = tbody.querySelector(".audio_duration");

See, querySelector, especially when using a class selector, only returns the first matched element. We need to use querySelectorAll here instead, which returns all of the matched elements.

Since querySelectorAll returns an array of multiple elements, we also need to handle the innerHTML setting a bit differently, so we use forEach to go through all of the matched elements and set the innerHTML on each of them.

Here's a snipped demonstrating the issue and the solution:

function problem() {
  let tag = document.querySelector(".duration");
  tag.innerHTML = "1:53";
}

function solution() {
  let tags = document.querySelectorAll(".duration2");
  
  tags.forEach((tag) => {
    tag.innerHTML = "8:41";
  });
}

function secure_solution() {
  let tags = document.querySelectorAll(".duration3");
  
  tags.forEach((tag) => {
    tag.textContent = "8:41";
  });
}

problem();
solution();
secure_solution();
<p>Problematic table</p>
<table id = "tbl">
  <tr><td class = "duration">0:00</td></tr>
  <tr><td class = "duration">0:00</td></tr>
  <tr><td class = "duration">0:00</td></tr>
  <tr><td class = "duration">0:00</td></tr>
  <tr><td class = "duration">0:00</td></tr>
  <tr><td class = "duration">0:00</td></tr>
</table>

<p>Working table</p>
<table id = "tbl2">
  <tr><td class = "duration2">0:00</td></tr>
  <tr><td class = "duration2">0:00</td></tr>
  <tr><td class = "duration2">0:00</td></tr>
  <tr><td class = "duration2">0:00</td></tr>
  <tr><td class = "duration2">0:00</td></tr>
  <tr><td class = "duration2">0:00</td></tr>
</table>

<p>Secure table</p>
<table id = "tbl3">
  <tr><td class = "duration3">0:00</td></tr>
  <tr><td class = "duration3">0:00</td></tr>
  <tr><td class = "duration3">0:00</td></tr>
  <tr><td class = "duration3">0:00</td></tr>
  <tr><td class = "duration3">0:00</td></tr>
  <tr><td class = "duration3">0:00</td></tr>
</table>

On an important side note you shouldn't use innerHTML to write the duration into the elements. Your duration is just text and contains zero html, so it is a better idea to use something like textContent instead. This ensures that some malicious party has no chance of injecting their own bad HTML to your page by somehow replacing duration data with HTML like <script>alert("hax")</script>. Notice how I actually just wrote HTML there, but StackOverflow has this thought out and your browser won't actually pop an alert box for you and only shows that as a text. If you ever DO need to use innerHTML, remember to sanitize it first.

CodePudding user response:

Your issue is because your selector .audio_duration is searching the tbody where all the .audio_duration elements are.

One solution would be to get the tr that you inserted and perform your selector on that and then it will look for .audio_duration in the tr instead of the tbody.

let tr = tbody.lastElementChild;
let trAudioDuration = tr.querySelector(".audio_duration");

CodePudding user response:

As explained by @Swiffy in his answer problem is there due to tbody.querySelector(".audio_duration"); as it will select first .audio_duration element from tbody.

You can update that query selector like below and it should work well.

let trAudioDuration = tbody.querySelector(`tr[tr-index='${i}'] .audio_duration`);

Above selector will try to find .audio_duration inside tr which has tr-index value as specified by i.

References :

  • Related