Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Kyle Fratello
Kyle Fratello
5,052 Points

How to solve this challenge without JQuery?

After completing this challenge(ajax-and-apis/stage-4-challenge) I wanted to solve it without JQuery to further solidify my understanding of Javascript and AJAX. I've got to be close but I just can't get it.I know I'm having issues with the preventDefault() method, among other things. Can anyone tell/show me how I can make this work? Also, is this unnecessary?I.E. Will I always want to rely on jQuery for creating similar AJAX requests to APIs? Thanks.

Here's what I have so far:

const submit = document.querySelector('#submit');

submit.addEventListener('submit', function (e) {
  e.preventDefault();
  const xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function displayPhotos(data) {
    if(xhr.readyState === 4 && xhr.status === 200) {
      let data = JSON.parse(xhr.responseText);
      let photoHTML = '<ul>';
      for (var i=0; i<data.items.length; i++) {
        photoHTML += '<li class="grid-25 tablet-grid-50">';
        photoHTML += '<a href="' + photo.link + '" class="image">';
        photoHTML += '<img src="' + photo.media.m + '"></a></li>';
      }
      photoHTML += '</ul>';
      };
      let photoUl = document.querySelector('#photos');
      photoUl.textContent = photoHTML;
  };
  xhr.open('GET', 'https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?');
  xhr.setRequestHeader("Content-Type", "application/json");
  let searchTerm = document.querySelector('#search').value;
  xhr.send(JSON.stringify({
        tags: searchTerm,
        format: "json"
      }));
});

Moderator edited: Markdown added so that code renders properly in the forums.

Steven Parker
Steven Parker
231,007 Points

Thanks mod! And so you'll know how to do it yourself, you can use the instructions for code formatting in the Markdown Cheatsheet pop-up below the "Add an Answer" area. :arrow_heading_down:   Or watch this video on code formatting.

2 Answers

Steven Parker
Steven Parker
231,007 Points

Actually, you seem to have gotten pretty close.

I found a few issues but had to guess at some others since the HTML isn't shown. The name "submit" made be a bit suspicious because it seems an odd name for a form ID. I'm wondering if you put that ID on the button instead of the form. If so, it would explain why the "preventDefault" didn't seem to work because the default behavior would be coming from the form itself.

But I made up some HTML to go with it, and gave the ID of "submit" to the form and the "preventDefault" worked as expected. I then identified an issue with the variable "photoHTML". It's declared inside the scope of the "if" block, but then an attempt is made to reference it after the block.

So I declared it in the outer scope and discovered an issue with CORS security. The response was being rejected because it was missing the "Access-Control-Allow-Origin" required from sites responding to cross-origin requests. I got around that temporarily by using a CORS proxy service:

  var proxy = 'https://cors-anywhere.herokuapp.com/';
  xhr.open("GET", proxy + "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?");

Then next I discovered that the response wasn't coming back in JSON form, but in XML. It could be that proxy service did not pass the query string correctly, but I also couldn't find that parameter in the API guide. Putting "format=json" instead did get a JSON response, but it was wrapped in parentheses following an object name. You may not encounter this issue if you resolve the CORS situation differently. But if you do, you could trim out the non-JSON bits before you run it through the parser.

As to whether you'd use jQuery for AJAX, it's certainly your choice; but I'd say that AJAX is where jQuery really shines. It makes things way easier than doing it in plain JS. I'll use jQuery it if it's already loaded, but otherwise I'll generally code in plain JS. But doing AJAX is one of the few things that will make me load it myself.

Kyle Fratello
Kyle Fratello
5,052 Points

Thank you so much Steven. Looks like a couple rookie errors on my part. That last part was a bit tricky for me though, surely wouldn't have been able to figure that out on my own. I updated my code how you suggested, added the proxy, and used slice method (xhr.responseText.slice(15,-1)); and that seemed to work. also I noticed I defined the variable 'data' then referenced it as 'photo' so fixed that too. now I'm getting an error that m of photo.media.m is undefined. not really sure what the issue was here. Looked like it could be another json formatting issue, so I tried to slice it again, but that didn't work out either. It's so close though! Anyway thanks again, Ive only been learning javascript for about a month now. things like this make it challenging but interesting to learn. I am definitely going to use jQuery for now, maybe until I can understand what's happening on a deeper level.

German Gamboa
German Gamboa
9,875 Points

What errors are you getting in the console?

Kyle Fratello
Kyle Fratello
5,052 Points

No errors in the console, however when I submit the search, the page refreshes, despite the preventDefault() method being used. I'm not sure why it doesn't work, as I used it accordingly to the mdn docs, atleast to the best of knowledge.

https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

German Gamboa
German Gamboa
9,875 Points

I would console log the event and see if it is targeting the submit. You might need to use event delegation (I think, it is hard to reason about it without your markup).