0

For my sins I am not a full-time JavaScript practitioner, but I am trying. Here is a simplified version of my attempted ad management namespace which uses the Google Publisher Tag API

slots is an array of GPT slots as returned by googletag.defineSlot(, set in the head which tries to target divs that only exist post-CMS HTML rendering by deferring it to jQuery.

How can I do this:

var i;
for (i=0;i<slots.length;i++) {
    jQuery(document).ready(function() { 
        jQuery('#ads').append('<div id="'+slots[i].getSlotElementId()+'"></div>');
    });
}

...with a dynamic i, because when I've been debugging it I've seen that I can't pass i like this, and can only pass a hard-coded index.

This works within the jQuery(document).ready(function() {:

console.log('adManager.all_devices.button.slots[3].getSlotElementId());

This causes undefined errors:

console.log('adManager.all_devices.button.slots[i].getSlotElementId());

I have been reading (honest) but I'm confused. In gentle-terms, what's going on here and how can I fix it?

ja_him
  • 417
  • 1
  • 4
  • 20
  • 1
    Why not just move the loop inside the handler function? – Pointy Oct 17 '18 at 14:27
  • Why though? I mean, if that's the right thing to do then I'm all for it, but I'm curious as to why it needs to be like that, is there a term that describes this? – ja_him Oct 17 '18 at 14:33
  • 1
    The problem with your code is that each of the separate "ready" handler callback functions *share* that same `i` variable. In other words, `i` inside each function refers to that very same `var i;` you declared. A given individual variable can only hold one value. There are other ways of fixing it, but in this case I don't see why you'd want to set up all those separate "ready" handlers anyway. – Pointy Oct 17 '18 at 14:41
  • OK cool, thanks. What can I search to read up more on why/how they share that same `i` variable? I see it what you describe in my code, but I would like to understand more (it doesn't seem logical to me, yet) – ja_him Oct 17 '18 at 15:30
  • 1
    [This is an older Stackoverflow post that has lots of information.](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example?rq=1) – Pointy Oct 17 '18 at 15:38

1 Answers1

3

You have a wrong construction. You need to attach ready listener once and then inside callback do your loop. As below:

  jQuery(document).ready(function() {
    for (var i=0;i<slots.length;i++) {
        jQuery('#ads').append('<div id="'+slots[i].getSlotElementId()+'"></div>');
    }
  });
MysterX
  • 2,318
  • 1
  • 12
  • 11
  • Why is that necessary? Is there a term that describes why I'm creating a problem doing it the way I have done it? – ja_him Oct 17 '18 at 14:32
  • Because `ready` listener is asynchronous but `for` loop is synchronous. I have no time to explain such wide theme – MysterX Oct 17 '18 at 14:34
  • 1
    The other simpler reason is that you only need to check that the document is ready once. Its callback is used to contain the rest of the code you want to run. – Andy Oct 17 '18 at 14:37
  • @Andy is that a separate reason? Or is that part of the reason MysterX gives above? What I mean is, looping on document.ready is pointless, as you said and I get that, but separately, the reason why using `i` there doesn't work is because jQuery queues up functions for when document *is* ready, and won't accept something synchronous within an asynchronous system. Is that right? – ja_him Oct 17 '18 at 15:16
  • OK I will make some time read [this](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) through, and explore from there – ja_him Oct 17 '18 at 15:41