0

I want to loop through Elements that was added asynchronously using fetch. Can somebody help? This is what I tried:

How I appended the Elements

    const body = document.querySelector("body");

    body.onload = function(){
        //loads the list of subjects
        getSubjects().then((data) => {
            let subjectSrting = ""; //initialize variable that holds the html of all the subjects semester wise

            // loops around each semester                
            data.forEach(function(semester){                    

                subjectSrting += "<div class='semester'> <div class='title yearTitle'>"+ semester.year +"</div> <div class='title semesterTitle'>"+ semester.semester +"</div>";

                // loops around each subject for each semester
                semester.subjects.forEach(function (subject){
                    let dis = ""
                    if(subject.type != "C"){
                        dis = "disabledSub";
                    }
                    subjectSrting += "<div class='subject "+dis+"'> <div class='sub'><span class='code'>" + subject.code + "</span><span class='name'>" + subject.name + "</span></div> <div class='subCredits'>" + subject.cred + "</div> <div class='attempt'> <input type='checkbox' id='" + subject.code + "' checked><label for='" + subject.code + "'>1st</label> </div> <button type='button' data_gpv='-1'  class='subResult'>SELECT</button> </div>";
                });                    

                subjectSrting += "</div>";

            });                

            let form = document.querySelector(".home form");
            form.innerHTML = subjectSrting;               

        })
    }

Here's where I find trouble to loop around the elements to get data-gpv values and to see if the checkboxes that were appended are checked or not:

    function calculateGpa(){
        let subjects = document.getElementsByClassName("home")[0].getElementsByClassName('subject');


        console.log(subjects.length); //logs 0, eventhough there are items in the HTMLCollection

        console.log(subjects); //logs a an HTMLCollection[] with the elements in it
        for(var i=0; i<subjects.length; i++){
            // get the gpv values and see if the chechbox is checked
            // cannot run this because subjects.length return 0
        }  
    }

    calculateGpa();
  • Subjects will be a collection of DOM nodes (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) and not an array. Try converting it to an Array using `const arr = [...subjects]`. – Ben Aston Feb 21 '20 at 16:00
  • I think this is a duplicate question [HTMLCollection showing 0 length](https://stackoverflow.com/questions/30211605/javascript-html-collection-showing-as-0-length) – asmaa Feb 21 '20 at 16:04
  • @ben Tried it didn't work... – Mohamed Arkib Feb 21 '20 at 16:05
  • @asmaa it sounds similar, but it isn't. I need to find a way to loop around the HTMLCollection that I get... – Mohamed Arkib Feb 21 '20 at 16:11
  • Double-check your selector. If the length is zero then you are not retrieving the DOM elements correctly, or at the appropriate time. – Ben Aston Feb 21 '20 at 16:30
  • @Ben when I console.log(subjects); It logs an HTMLCollections which contains all the elements that I need (to get the data from). but I am unable to access the data from the var subjects. Even subjects.item(0) returns null – Mohamed Arkib Feb 21 '20 at 17:18
  • This sounds like a race condition. Your call to `calculateGPA` must occur in a then, after the loading has completed, to ensure that you only try and perform the calculation after the asynchronous operation has completed. – Ben Aston Feb 21 '20 at 17:48

1 Answers1

1

This sounds like you are not waiting for the DOM to be populated with the result of the asynchronous call, before running the GPA calculation.

You can either, use the Promise API (then) to fix this, or you can supply a callback to onLoad.

function onLoad() {
    getSubjects()
        .then((result) => {                 
            document.querySelector(".home").innerHTML = result              
        })
        .then(calculateGPA)
}

function getSubjects() {
    return new Promise((resolve) => setTimeout(() => resolve(`
        <div class="gpa">2.0</div>
        <div class="gpa">3.0</div>
    `), 3000))
}
    
function calculateGPA() {
    let total = 0
    const gpas = document.getElementsByClassName('home')[0].getElementsByClassName('gpa')

    console.log(gpas.length) // 2
    console.log(gpas) // HTMLCollection
    
    for(let g of [...gpas])
        total += Number(g.innerText)
        
    console.log(`The average gpa is: ${(total/gpas.length)}`) 
}


onLoad()
<div class="home">
 Waiting for GPAs...
</div>
Ben Aston
  • 53,718
  • 65
  • 205
  • 331