Loop and API call help (Loop assigns max value?)

Hello, all. I’m trying to add event listeners to display some text to buttons that are being generated after making an API call. I would like each button generated to show that user’s complete profile. However, my loop keeps assigning “10” as the value of “i” to everything.

Here’s the code (using jQuery):

// Application
var rawData = {};
var app = {
  init: function() {
    let buildHTML = "";
    $.getJSON("http://jsonplaceholder.typicode.com/users", function(data){
    data.forEach((x, index) => {
      buildHTML += (`<div class="entry"><span class="name"><a href="mailto:${x.email}">${x.name}</a></span>, Entreprise : ${x.company.name} <button class="full" id="user${index}">Profil</button></div>`)
    })
    $("#users").html(buildHTML);
    for (var i = 0; i < data.length; i++){
      console.log(i)   
      $("#user"+i).on("click", () => $("#profile").text(i))}
    })
  },
};

// quand le DOM est prêt, on lance app.init
$(app.init);
document.addEventListener("DOMContentLoaded", function(){
  document.getElementById("load").addEventListener("click", app.init);
});

Any idea of where I’m going wrong?

Here’s a screen capture of the ouput:

To be clear, what I want to be happening is that if the user clicks on the the button next to the username, they’ll see the index number (i.e. clicking on the first user would make “0” appear below the box, clicking on the second user would make “1” appear and so on).

I worked around it by using this code instead:

    $(".full").each(x => $("#"+x).on("click", function(){
      $("#profile").text(x)
    }))

And it works as expected. Though honestly, I have no idea why the original method wouldn’t work so if someone wants to drop a comment just to help a guy out, but otherwise, I’ve got it working as expected.

There is a delay between executing your loop and executing your click-function - that happens when you click. In that moment the loop is almost done and i has it’s final value: 10. And that value is used within click-function.
Probably there is a solutiion with a so called bind()-function, which binds that current i to your click-function but your new solution looks better and easier.

Yeah, that was my suspicion.

FIrst and foremost, thank you for your response. However, there’s two things I still don’t quite understand: Why did it assign all of the buttons the value of “10” and not just the last one and why did my solution work, because unless jQuery is doing something behind the scenes, .each is essentially a for loop, right?

First question:

    for (var i = 0; i < data.length; i++){
      console.log(i)   // [1] i = 0
      $("#user"+i).on(
           "click", 
           () => $("#profile").text(i)) // [2] NOTHING HAPPENS HERE WHILE LOOPING
    }

My interpretation for the first iteration of the loop (no warranty…):

  • loop starts
  • i is 0
  • console shows: 0
  • click event is added to #user0
  • This is NOT executed $("#profile").text(i)
  • looping continues and ends
  • final value of i = 10

Then you click on #user0

  • $("#profile").text(i) is executed with i=10

Second question:
As far as I understand: this comes from Arrow-Notation. This notation is also new stuff for me so I created a pen playing through 3 szenarios:

  1. adding click with bind()
  2. using arrow-notation (without jquery): this works too, so it has nothing to do with jQuery!
    I can’t explain what’s happening behind the scene but somehow Arrow-Notation uses something similar to bind
  3. arrow-notation with jQuery
1 Like

Try using let instead of var.

1 Like

This worked! And it is simple.

@s-projects18
Sorry for taking so long to get back to you! It was a really great explanation and I wanted to make sure I digested it fully before I responded. Thanks to your fiddle, I see a bit more of what’s going on. I had to add some other console.log statements to help me see all the steps (and refresh my memory on exactly how call and bind work), but after sitting down and really looking at the three, it makes a lot more sense. Thank you!

@lasjorg
Hahaha Sometimes the simplest answer is best! I think I got confused thinking that when we declare a for loop like this:

for (i=0; i < x.length; i++)

That is to say, without var or let, I thought it defaulted to let, not var, so that’s another thing I learned today! I’m still trying to see exactly why that works though. I know in @s-projects18’s response we established that the loop runs through and gets stuck at 10. I can kind of intuit how changing it to let would work past this, but… truthfully, I don’t see it crystal clear. Can you help me out with a few details to help fill the gaps?

@s-projects18

As for arrow notation, I’m probably not going to give the clearest explanation but I know that in arrow notation, the this value is taken from some other context that the function itself. Here’s a relevant quote from the FCC docs:

An arrow function doesn’t define it’s own this value, it inherits this from the enclosing function:

That’s probably not super helpful but I like to try to payback whatever I can! (Personally, when this gets involved, I just start console.log-ging until I know what’s going on. :p)

1 Like

It is because of let being block scope, I think this page or this page explains it pretty well.

1 Like

Yep, that makes sense: this is no longer “overwritten”. Thanks.