Question

I've tried searching for this issue and have found a number of questions from people having problems with their for loops and it only returning the last element. Try as I might, I'm just not understanding what I'm doing wrong here.

I'm really new to Javascript so I think I'm having an issue with closures? What I'm writing is supposed to grab some data from the JSON returned from the Urbandictionary API. The JSON fetch works but my for loop to build divs with that data is not working properly.

function word(term, def, example)
    {
      this.term = term;
      this.def = def;
      this.example = example;
     }

var lexicon = ['highway+salute', 'score', 'ya+heard+me', 'utz'];

var color = ['reddy', 'bluey', 'greeny', 'tanny', 'darky'];

for (var i = 0; i < lexicon.length; i++) {

$.getJSON('http://api.urbandictionary.com/v0/define?term=' + lexicon[i],
    function(data){
        lillyLivered = [];
        lillyLivered.push(new word(data.list[0].word, data.list[0].definition, data.list[0].example));
        console.log(lillyLivered);

        $('.container-fluid').html('<div class="row message unread">' + 
        ' <div class="col-xs-12 col-sm-6 col-lg-3 malok ' + color[i] + ' id="">' +
        '<div class="row dict">' + 
        '<div class="col-xs-9 col-sm-9 col-lg-9">' + 
        '<h3 class="term term0">' +
        lillyLivered[i].term + 
        '</div><div class="col-xs-3 col-sm-3 col-lg-3">' +
        '<div class="col-xs-3 col-sm-3 col-lg-3">' + 
        ' <span class="step size-32"><i class="icon ion-ios7-world"></i></span>' +
        '</div></div>' + 
        '<div class="row dict"><div class="col-xs-12 col-sm-12 col-lg-12">' +
        '<p class="definition definition0">' + 
        lillyLivered[i].def +
        '</p><p class="eg eg0">' + 
        lillyLivered[i].example + 
        '</p></div></div></div>'
        );
    }
)};

I have a fiddle up here: http://jsfiddle.net/orenthal/5X96B/

This code works somewhat if I use lillyLivered[0]. It doesn't load at all if I use lillyLivered[i]. I get the following error:

Uncaught TypeError: Cannot read property 'term' of undefined (anonymous function)

I'm getting confused by it. Console output of lillyLivered only shows the item at the index position 2 in the lillyLivered array. In this case "ya heard me":

lillyLivered
[ word
    def: "Do you understand me? [ya heard] popular in New Orleans. the phrase is suppose to come after a sentence."
    example: "Say brah, I'm chillin here in Orlando, ya heard me. ↵-C-murder's innocent, ya heard me."
    term: "ya heard me"

I thought that maybe the problem existed because of the calls to lillyLivered[i].term etc... were contained within the same for loop as the calls to the JSON data? So I tried doing this with two loops.

I created a new for loop that would take care of creating the divs and outputting the items from the lillyLivered array but I ran into the same issue here.

Uncaught TypeError: Cannot read property 'term' of undefined 

But this has helped with one of the issues, that being that the console output of lillyLivered now lists all four words from lexicon along with their definitions and examples.

lillyLivered
[word, word, word, word]

I have a fiddle of this one up here: http://jsfiddle.net/orenthal/5bTrJ/

I get the feeling that I'm being dense and missing something very obvious here.

Was it helpful?

Solution

Yes your issue has to do with closures. Your get requests are likely finishing (and calling their respective callbacks) long after all of the requests have been sent. Because of this, by the time the callbacks are called, i will be equal to lexicon.length. You can fix this by enclosing the get request in its own functional scope and passing the index i at that moment into that scope. Like this:

for (var i = 0; i < lexicon.length; i++) {
    (function(i) {
        $.getJSON('http://api.urbandictionary.com/v0/define?term=' + lexicon[i],
            function(data){
                lillyLivered = [];
                lillyLivered.push(new word(data.list[0].word, data.list[0].definition, data.list[0].example));
                console.log(lillyLivered);

                $('.container-fluid').html('<div class="row message unread">' + 
                ' <div class="col-xs-12 col-sm-6 col-lg-3 malok ' + color[i] + ' id="">' +
                '<div class="row dict">' + 
                '<div class="col-xs-9 col-sm-9 col-lg-9">' + 
                '<h3 class="term term0">' +
                lillyLivered[i].term + 
                '</div><div class="col-xs-3 col-sm-3 col-lg-3">' +
                '<div class="col-xs-3 col-sm-3 col-lg-3">' + 
                ' <span class="step size-32"><i class="icon ion-ios7-world"></i></span>' +
                '</div></div>' + 
                '<div class="row dict"><div class="col-xs-12 col-sm-12 col-lg-12">' +
                '<p class="definition definition0">' + 
                lillyLivered[i].def +
                '</p><p class="eg eg0">' + 
                lillyLivered[i].example + 
                '</p></div></div></div>'
                );
            }
        )};
    }(i));
}

The other answer also mentions a good point about lillyLivered.

Edit: Due to high demand :D, you could also use the forEach method to clean up your code and fix your issue:

lexicon.forEach(function(lexItem, i){
            $.getJSON('http://api.urbandictionary.com/v0/define?term=' + lexItem, // lexItem === lexicon[i]
                function(data){
                    lillyLivered = [];
                    lillyLivered.push(new word(data.list[0].word, data.list[0].definition, data.list[0].example));
                    console.log(lillyLivered);

                    $('.container-fluid').html('<div class="row message unread">' + 
                    ' <div class="col-xs-12 col-sm-6 col-lg-3 malok ' + color[i] + ' id="">' +
                    '<div class="row dict">' + 
                    '<div class="col-xs-9 col-sm-9 col-lg-9">' + 
                    '<h3 class="term term0">' +
                    lillyLivered[i].term + 
                    '</div><div class="col-xs-3 col-sm-3 col-lg-3">' +
                    '<div class="col-xs-3 col-sm-3 col-lg-3">' + 
                    ' <span class="step size-32"><i class="icon ion-ios7-world"></i></span>' +
                    '</div></div>' + 
                    '<div class="row dict"><div class="col-xs-12 col-sm-12 col-lg-12">' +
                    '<p class="definition definition0">' + 
                    lillyLivered[i].def +
                    '</p><p class="eg eg0">' + 
                    lillyLivered[i].example + 
                    '</p></div></div></div>'
                    );
                }
            )};
});

OTHER TIPS

You are redefining lillyLivered every time you call getJson so there won't be an ith term. Change this:

for (var i = 0; i < lexicon.length; i++) {

$.getJSON('http://api.urbandictionary.com/v0/define?term=' + lexicon[i],
    function(data){
        lillyLivered = [];

To:

var lillyLivered = [];
for (var i = 0; i < lexicon.length; i++) {

    $.getJSON('http://api.urbandictionary.com/v0/define?term=' + lexicon[i],
        function(data){...}
}

If I am reading your issue correctly you want to have all of the results displayed on the page at once and you are only seeing the last one?

if that is the issue the problem is that you are overwriting your div each time you get a new item from the api. if you change the code you have that writes the html to the dom with .html:

$('.container-fluid').html(...);

to use .append instead you will see all the results on the page:

$('.container-fluid').append(...);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top