0

I'm looping a set of div's conaining an id for YouTube videos to get and show their titles, descriptions, etc. The divs I loop through look like this:

<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>
<div class="tutorialVideoDataContainer" data-clipid="xxxxxxxxxxx"></div>

For some reason the loop adds all of the info to the last of these divs instead of applying the info to the current div inside the loop. Any idea why this is?

And also, as a sub question, why can't I reach i inside the ajax call? I have to set tutorial = tutorials[i] and append the children to tutorial instead of tutorials[i] but I can't use just tutorials[i] within the ajax part. I can't understand what the difference is.

(function () {
    var tutorials = document.getElementsByClassName('tutorialVideoDataContainer');

    for (var i = 0; i < tutorials.length; i++) {
        var tutorial = tutorials[i];

        console.log(i);
        console.log(tutorial);
        console.log(tutorials[i]);

        var id = tutorial.getAttribute('data-clipid');

        $.ajax({
            url: "http://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=json",
            dataType: "jsonp",
            success: function (data) {
                var title = data.entry.title.$t;
                var description = data.entry.media$group.media$description.$t;
                var duration = data.entry.media$group.media$content[0].duration;
                var thumbnail = data.entry.media$group.media$thumbnail[3].url;

                if (title.length > 0) {
                    var titleElement = document.createElement("h3");
                    titleElement.innerText = title;
                    tutorial.appendChild(titleElement);
                }

                if (description.length > 0) {
                    var descriptionElement = document.createElement("p");
                    descriptionElement.innerText = description;
                    tutorial.appendChild(descriptionElement);
                }

                var minutes = Math.floor(duration / 60);
                var seconds = duration - minutes * 60;

                var durationElement = document.createElement("p");
                durationElement.innerText = minutes + ":" + seconds;
                tutorial.appendChild(durationElement);

                var clickLink = document.createElement("a");
                clickLink.className = "showOverlayVideo";

                var thumbnailElement = document.createElement("img");
                thumbnailElement.src = thumbnail;
                thumbnailElement.alt = title;
                clickLink.appendChild(thumbnailElement);

                tutorial.appendChild(clickLink);
            }
        });
    }
})();
jcubic
  • 61,973
  • 54
  • 229
  • 402
Peter
  • 3
  • 4
  • 4
    possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Scimonster Apr 28 '15 at 13:51
  • 1
    perhaps a jsFiddle will help illustrate your problem set better. – JasonWilczak Apr 28 '15 at 13:52
  • Might be this: before ajax has completed, i value has become 4 (_for_ bucle is fast compared to ajax calls), but this is the same value for the three ajax calls. – Fernando Apr 28 '15 at 13:56
  • @JasonWilczak I'm working on it! – Peter Apr 28 '15 at 14:02

3 Answers3

2

If you loop over array and crate closure where you access the index of the array you access reference to variable not copy of it so when all asynchrous code is finished you end up with reference to last element. To fix it you need to create another closure with new reference:

for (var i = 0; i < tutorials.length; i++) {
    (function(tutorial) {
        // you can use tutorial inside
    })(tutorials[i]);
}
jcubic
  • 61,973
  • 54
  • 229
  • 402
0

Because of the way the javascript event loop works, the loop finishes running before your ajax requests come back. So by the time they do the variable tutorial is set to the last one in the loop.

Does the data you get back from the youtube api include the clip ID, you could then use that to look up the correct dom element in the callback rather than inside the loop.

Stuart Leigh
  • 826
  • 4
  • 6
0

My guess is that since $.ajax() is not a blocking function, the loop is finishing before ajax is done doing it's thing. This would cause the execution path to look something like this:

  1. Loop starts, i = 0
  2. Ajax call is made
  3. i = 1
  4. i = 2
  5. ...
  6. i = tutorials.length
  7. Ajax call finishes

Hope that helps / makes sense.

Tyler
  • 304
  • 1
  • 3
  • 12