1

I have a tab element in HTML that list data from 2 json files that looks like the following:

json1.json

{
    "title" : "json1 title",
    "json1_data" : 
    [
        {
            "info1": "AAA",
            "info2": "BBB"
        },
         *more elements here*
        {
            "info1": "YYY",
            "info2": "ZZZ"
        }
    ]
}

json2.json

{
    "title" : "json2 title",
    "json2_data" : 
    [
        {
            "info1": "111",
            "info2": "222"
        },
         *more elements here*
        {
            "info1": "333",
            "info2": "444"
        }
    ]
}

These 2 json files contain json1_data and json2_data respectively that has the same structure (that is, with indices info1 and info2). So, I created "common" functions to process these json data.

async function fetchData(path){
    // try fetch object from json file
    try {
        let response = await fetch(path);
        return response.json();
    } catch (error) {
        console.log(error);
    }
}


async function fetchJson1(){
    let myData = await fetch('json1.json'); 
    return myData.json1_data;
}

async function fetchJson2(){
    let myData = await fetch('json2.json'); 
    return myData.json2_data;
}

async function myFunction(idx){
    let i;
    let listObj;

    if(idx==1){
        listObj = await fetchJson1();
    }else{
        listObj = await fetchJson2();
    }

    let tabContent = document.getElementById("tab" + idx);

    let container = document.createElement("div");
    for(i=0; i<listObj.length, i++){
        let infoRow= document.createElement("div");
        let info1 = document.createElement("span");

        // display info1
        info1.innerText = listObj[i].info1;
        
        // test if info2 read is OK
        console.log("outside: " + listObj[i].info2);

        // add onclick action to info1
        info1.onclick = function(){
            // test if info2 read is OK
            console.log("inside: " + listObj[i].info2);  <-- problem part
        }

        infoRow.appendChild(info1);
        container.appendChild(infoRow);
    }
    tabContent.appendChild(container);    
}

HTML file looks like the following:

index.html

<button id="btn1" onclick="myFunction(1)"> Show json1 content </button>
<button id="btn2" onclick="myFunction(2)"> Show json2 content </button>
<div id="tab1">
<div id="tab2">
<script>
    document.getElementById("btn1").click();
</script>

Basically there is no problem with the listing of objects both from json1.json and json2.json when either button is clicked. Moreover, "onclick" function for json2 works fine (displays "inside: ") in console. However, when I click on the display for json1, this onclick function does not work and says "Uncaught TypeError: Cannot read properties of undefined (reading 'info2') at info1Element.onclick". How come listObj[i].info2 is fine for "listObj = await fetchJson2()" but not for "listObj = await fetchJson1()"?

I verified there is no problem with both json files. I suspect it has something to do with the "common" listObj that can take 2 different arrays of objects (although they have the same structure). Any idea how to fix this? (if possible, exclude coding myFunction twice for each json file since there can be more json files)

I tried changing the position of the following:

    if(idx==1){
        listObj = await fetchJson2();
    }else{
        listObj = await fetchJson1();
    }

In this case, "onclick" function for json1 info1 works but the "onclick" function for json2 info1 did not.

franz
  • 11
  • 2
  • Use `for(let i=0; `, and remove the `let i`, otherwise you only ever get one `i` variable for all iterations of your loops, so the `i` used within your function will change and point to `listObj.length` (an invalid index) once you're done looping. Related: [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/q/750486) and [Explanation of \`let\` and block scoping with for loops](https://stackoverflow.com/a/30900289) – Nick Parsons Apr 17 '23 at 11:14
  • You have a typo: `info1.innetText` should be `info1.innerText` – motto Apr 17 '23 at 11:15
  • @NickParsons Whoa it worked! Thank you so much! How can I flag this as the right answer? Or should I delete this question instead since it is so out of topic? – franz Apr 17 '23 at 11:51
  • 1
    @franz You can't accept a comment as an answer, I believe this is answered somewhere else more fully (if not in the links I've linked). So while your question may be marked as a duplicate you should still leave it up (as its still on-topic and will help act as a sign-post for future people who find this question). – Nick Parsons Apr 17 '23 at 12:11
  • 1
    The more duplicates there are which are correctly flagged the better. Search engines often list duplicate questions as the top result for a given search. Your question will help others, since it’s much more likely that someone else experiencing this issue searches for the error message it causes rather than “closures simple practical”. – James Apr 17 '23 at 15:18

0 Answers0