Ajax request inside click function?

Can you run an ajax request inside of a click function? I have been working with an api and I need to do a second request to get the comments for an image/video that was clicked. I am really new with ajax request and api’s. And I have been working on a small script piecing things together.

First off I know I am not accessing the url correctly for the second ajax request. I dont know how to re-use the code for that inside of this click event (if thats even where it goes). Second I dont know the proper way to access the array of comments. You can see in my butchered up code below I am lost for this part of the functionality.

My console error message is this - "GET http://localhost/ig/insta.apiUrl 404 (Not Found) "

I know I have things wrong please bare with me.

(function () {
    "use strict";
    var insta = window.insta = window.insta || {}; // insta Object

    insta = {

        apiUrl: 'https://api.instagram.com/v1/users/self/feed?access_token=[token here]&callback=?',

        init: function () {
            $.ajax({
                type: 'GET',
                url: insta.apiUrl,
                dataType: 'json',
                success: insta.success
            });
        },

        renderGrams: function (grams) {
            $.each(grams, function(index, gram) {
                var gramHtml = '<div class="col-md-3"><p><img class="img-circle" style="margin-right: 5px" width="60" src="' + gram.user.profile_picture + '">' + gram.user.username + '</p><a href="#myModal" data-toggle="modal" data-username="' + gram.user.username +'" data-img-url="' + gram.images.standard_resolution.url + '" data-tags="' + gram.tags + '">';
                if (gram.type === 'image') { gramHtml = gramHtml + '<img class="img-thumbnail" src="' + gram.images.low_resolution.url + '"/>'; }
                if (gram.type === 'video') { gramHtml = gramHtml + '<video class="img-thumbnail" src="' + gram.videos.low_resolution.url + '"/>'; }
                gramHtml = gramHtml + '</a></div>';
                $('.results').append(gramHtml);
            });
        },

        setupClickEvent: function () {
            $('a').click(function(){
                var largeImage = $(this).data('imgUrl');
                var username = $(this).data('username');
                var tags = $(this).data('tags');

                $('.modal-title').html(username);
                $('.modal-body').html('<div class="col-md-6"><img class="img-responsive" src="' + largeImage + '"><div class="tagwrap">' + tags + '</div></div>');

                $.getJSON('insta.apiUrl', function(index, gram) {
                $('.comments-container').html('<p>' + comments.data.text + '</p>');
                });

            });
        },

        success: function (responseData) {
            if (responseData.meta.code === 200) {
                insta.renderGrams(responseData.data);
                insta.setupClickEvent();
            } else {
                $('.results').html('<h1>An Error Occured</h1><p>' + responseData.meta.error_message + '</p>');
            }
        }
    };

    // This actually fires fires everything off
    // Follow from here
    insta.init();
}());

Here is some of the json for the comments -

"comments": {
        "count": 6,
        "data": [
          {
            "created_time": "1391457298",
            "text": "First like xd",
            "from": {
              "username": "lars010999",
              "profile_picture": "http:\\/\\/images.ak.instagram.com\\/profiles\\/profile_248947423_75sq_1379584795.jpg",
              "id": "248947423",
              "full_name": "Lars Lopulalan"
            },
            "id": "647913236748481826"
          },
          {
            "created_time": "1391457540",
            "text": "@blamay48 @bhamilton543 is a for sure character!!\\ud83d\\ude02 #tourguide",
            "from": {
              "username": "mepmx",
              "profile_picture": "http:\\/\\/images.ak.instagram.com\\/profiles\\/profile_340656728_75sq_1382493493.jpg",
              "id": "340656728",
              "full_name": "MEPMX\\ud83d\\udcf7@mepmx"
            },
            "id": "647915264509916589"
          },

With the line:

$.getJSON('insta.apiUrl', function(index, gram) {

try removing the quotes arount insta.apiUrl

Thank you. That worked I believe but now I have an error - Uncaught ReferenceError: comments is not defined

So I need to define it in with a var I assume? Why is it that I cant seem to build things yet with my brain based off of the books/tutorials I have done haha, this is killing me!

My placement may be a bit off as well, but I’m really stumped on putting another ajax request together for this. All the ajax examples, and reading the docs don’t even help me “get it” when trying to piece this together.

Hi clayton47,

It looks like this section of code is the problem:

$.getJSON(insta.apiUrl, function(index, gram) {
    $('.comments-container').html('<p>' + comments.data.text + '</p>');
});

the success callback should have a single argument called comments:

$.getJSON(insta.apiUrl, function(comments) {
    $('.comments-container').html('<p>' + comments.data.text + '</p>');
});

Ah ha! That returned some legitimate data - But I am trying to access it incorrectly it looks like. I’m not sure how to write the function to get the comments text array. And each image I click on after closing the modal that pops up my image with data its showing in the console that its just adding the next set of comments ontop of the next. How do I reset that ajax call for each click so it only displays data related to that item clicked?

Looks like this is getting closer. Thanks for the help!

Here is the console log I did on the (comments) function.

Object {pagination: Object, meta: Object, data: Array[19]}
data: Array[19]
0: Object
1: Object
2: Object
3: Object
4: Object
5: Object
6: Object
7: Object
8: Object
9: Object
10: Object
11: Object
12: Object
13: Object
14: Object
15: Object
16: Object
17: Object
18: Object
attribution: null
caption: Object
comments: Object
count: 11
data: Array[8]
0: Object
created_time: "1391489255"
from: Object
id: "648181310245154586"
text: "@jay_lyne I'm bringing ear plugs this year so my ears don't bleed from all of you screaming. Lol jk"
__proto__: Object
1: Object
2: Object
3: Object
4: Object
5: Object
6: Object
7: Object
length: 8
__proto__: Array[0]
__proto__: Object
created_time: "1391487831"
filter: "Normal"
id: "648169368524795843_181493434"
images: Object
likes: Object
link: "http://instagram.com/p/j-wptFNa_D/"
location: null
tags: Array[1]
type: "image"
user: Object
user_has_liked: false
users_in_photo: Array[0]
__proto__: Object
length: 19
__proto__: Array[0]
meta: Object
pagination: Object
__proto__: Object

OK, so I’ve read over your code and had a look at the Instagram API docs to see what actually gets returned in the JSON response. I’ve made some changes to your code which should hopefully get it working the way you want (although I haven’t been able to test it as I don’t have an Instagram account or API key).

First of all, add a grams object to your insta object, which we’ll use to cache the data returned from the API:

insta = {

    grams: {},

    // ...

Next, within the renderGrams function, add this line of code to add each item to our cache object, using the ID as a key:

$.each(grams, function(index, gram) {
    insta.grams[gram.id] = gram; // Add item to cache
    // ...

and, change the modal link HTML so that it has a data attribute only for the item ID:

<a href="#myModal" data-toggle="modal" data-media-id="' + gram.id +'">

Next, within the setupClickEvent function, we need to grab the ID from the clicked link, and use that to pull the rest of the data from the cache:

$('a').click(function(){
    var mediaId = $(this).data('mediaId'),
        gram = insta.grams[mediaId],
        largeImage = gram.images.standard_resolution.url,
        username = gram.user.username,
        tags = gram.tags,
        comments = '';

then, at the bottom of the function, instead of making an extra AJAX call to get the comments, we’ll pull the comments from the cache and loop over them, appending them to a string which we then inject into the page:

$.each(gram.comments.data, function(index, comment){
    comments += '<p>' + comments.text + '</p>';
});

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

I’ve created a gist of the whole thing, so you can see the changes in context: https://gist.github.com/anonymous/8810353

Ok, I was aware of trying to store them in a data attribute. But is it bad to try to store all the comments for all the grams loaded on a page into that? Forgive my ignorance, but I thought it would be better to retrieve them upon click because of that. By default 18 instagram items are loaded on the page. Couldn’t it add up to a lot of comments being stored in the cache? Maybe I need to look up the cache usage in the docs for json data like this (if I am understanding this correctly).

How would we test speed of this type of web app to know what might be more efficient in code? I have never even performed tests on any code so thats a whole other world I assume.

I really appreciate all the help everyone has contributed to this. My brain is trying to soak up everything!

I tried the code from your gist, and something isnt working. No error messages in the console or anything. I noticed on line 44 in the gist -

comments += '<p>' + comments.text + '</p>'; 

should there be an = after the + ? I removed it and code still didnt work.

I wouldn’t have thought it’d be a problem, but perhaps someone else could comment on that?

It’s not so much to do with speed (although it does help) as cutting down the number of calls to the API - lots of APIs use rate limiting so it’s good to economize on requests, especially if you’re anticipating a lot of users.

Ah, sorry, that’s a typo. It should be:

$.each(gram.comments.data, function(index, comment){
    comments += '<p>' + comment.text + '</p>';
});

Ah, sorry, that’s a typo. It should be:

$.each(gram.comments.data, function(index, comment){
    comments += '<p>' + comment.text + '</p>';
});

[/QUOTE]

Thats the same as it was, is that correct syntax? I am still pretty new so some stuff I may not recognize being a noob.

I am still not able to get it to work. I will pm you a client id.

Sorry, I should have pointed out the difference… I was referring to comments (plural) within the loop rather than comment .

Thanks, I’ll take a look.

Hey man,

So, here’s the updated gist: https://gist.github.com/anonymous/8813601

I’d made another typo while working with the code (I changed insta.apiUrl to something else, and forgot to change it back), but there was also another problem with the comments container being overwritten every time a modal is created in the click handler.

The code seems to work fine for me now.

Sweet, thanks it works!

I implemented a bootstrap grid class and its styled a bit. One thing I noticed with my code is the items that are videos are not playing. It just displays the poster image for them like the rest of the images… Do we need to add to the click event for this to happen? The renderGrams function above that has -

if (gram.type === 'video') { gramHtml = gramHtml + '<video class="img-thumbnail" src="' + gram.videos.low_resolution.url + '"/>'; }

On the code you have changed can you explain what you have done so I can understand better. Being that I am so new to jQuery, and especially ajax stuff its hard for me to just start building things I want. I want to know whats making things work.

Hi,

Sorry for the delayed reply.

I’m not that familiar with using video in the browser, but I think you can add the ‘controls’ attribute to the video tag to allow users to start/stop the video. So the above line would change to:

if (gram.type === 'video') { gramHtml = gramHtml + '<video class="img-thumbnail" src="' + gram.videos.low_resolution.url + '" controls />'; }

I didn’t change much… just the last line of the setupClickEvent function. Originally, the code was setting the contents of div.comments-container:

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

but the problem is you already overwrote the comment container markup a few lines previously, with this line:

$('.modal-body').html('<div class="col-md-6"><img class="img-responsive" src="' + largeImage + '"><div class="tagwrap">' + tags + '</div></div>');

So, I changed the last line of the function to wrap the comments in a new container div and append that to the modal body element:

$('.modal-body').append('<div class="comments-container">'+comments+'</div>');

Thanks again so much for the help. (I didnt get this message in my email that you replied until now. All my emails for replies to this thread were daaaaaaays behind). I am starting to get more familiar with this stuff. All the documentation, and tuts out there never cover unique things like this. They are always the ABC’s of working with a language and their different functions etc… I have been building this lil simple app out and its fun and over educational at times doing what seems to me more advance stuff than what a noob like myself is capable of at the time. A little bit of help here helps jump start things and explains them really well. We have all been there before. We have something in our mind we want to do, but just cant seem to wrap our minds around the correct code!

Thanks again for showing me a way to do what I was trying to get accomplished!