Free Code Camp — Wikipedia Viewer

Live Link

(if you want to skip the reading and get right to the finished product!)


Free Code Camp required user stories:

  • I can click on a button to view a random article
  • I can use a search-bar to search for a wikipedia article and have its link returned to me

Personal goals:

  • learn the wikipedia / wikimedia API
  • enable an autocomplete feature for searches
  • display summary text and and a link for the auto completed searches

Phase 0 — Research

Wikimedia API relevant tools:

Outside Resources:

  • Finding an autocomplete action was not very easy on the actual wikipedia API site. Because as usual I am so inept I can’t even form the right question. Obviously I resorted to google and came across this (extremely!) pertinent article.
  • jQuery UI Autocomplete Documentation

Phase 1 — Mucking About

Getting autocomplete to work (learning how autocomplete works…)

  • I do not like to just copy and paste code, even when it’s sitting there on a shiny platter. In my previous project I had been able to get away with $.get requests (for location / weather) so I thought well why bother with the full $.ajax approach used in the w3Lesson. I’m a smart guy, right? I clearly know better than a fellow writing tutorial articles.
  • Why bother — because it won’t work otherwise, dumbass.

Short of the long story — $.get is a shorthand for $.ajax request and includes a lot of default behavior to let you get away with that shorthand. That won’t work in this case unless you like to hit your head against the ‘No Access-Control-Allow-Origin’ header is present error. I’m not going to pretend I understand what that means at this point. I know it’s bad because Father Console Log yelled at me in bold red.

But here’s a helpful Wikipedia article and StackOverflow response on the subject. Bottom line — I had to learn how to generate a full $.ajax request.

So this meant that whatever my own approach was to this problem wasn’t going to work. And I was faced with copying code. Shudder.

This brings me to a topic that I have wanted to discuss for a while about copying code. I am adamantly against copying code because it feels like cheating both to myself and to anyone I present / deliver my projects to. I have never been a thief and I won’t ever become one as I go down the road of my, albeit recent, but honestly biggest, passion in life — coding.

I have a lot of pride in knowing that everything I have made and put out has been done by my own hand — no matter how battered and bruised that hand has become from frustrated beatings.

At the same time I can’t expect to keep reinventing the wheel along the way. As much as I want to feel the sense of pride in producing unique [self] solutions I also need to be realistic and efficient with my time if I’m ever to find a job in this industry. There is no need to reinvent the wheel — as long as I understand how the wheel works and don’t pass it off as my own invention.

So if I was going to use this code I found I needed to at the very least break it down and learn how that ‘wheel’ functions.

Step one of breaking down this code was to find out what the hell the request and response parameters were doing. Well even further back — I needed to learn how autocomplete’s ‘source’ attribute worked. So I started here:

search_bar.autocomplete({
    source: function(query, result){
        console.log(query);
    }
});
// search_bar = $('#search_bar')

What came of this was pretty cool. It was logging every letter I typed into my search bar. Makes total sense once I saw it but it was a learning experience for me. Okay so I now know how the the autocomplete worked…sort of?

Autocomplete — source: query

As you can see it was returning an object with a term parameter who’s value was each letter I was typing (what actually needed to be passed to the autocomplete source). So I realized I couldn’t just pass the query right into the $.ajax request to wikipedia — I had to pull out that term (using the handy object dot-notation) directly.

search_bar.autocomplete({
source: function(query, result){
console.log(query.term);
}
});

Now we’re getting somewhere!

Autocomplete — source: query.term
(comment on the w3L page) Oh hey Fabien from 8 months ago that I have no way of contacting…

Alright so I got the first part down. Now to figure out everything else that was going on in this $.ajax method (request, function? I am still a noob with the terminology…). I have added all of the comments in the code below with some minor modifications as I tried to retype what I found (it’s the only way I’ll sleep at night).

If I’m going to copy code I have to rebuild it line by line and feel confident that I understand every bit of it by commenting it out— then at least I’ve learned from ‘copying’.

search_bar.autocomplete({
source: function(query, result) {
        $.ajax({
     // Wiki API endpoint
url: "http://en.wikipedia.org/w/api.php",
     // JSONp return to "relax the same-origin-policy"
dataType: "jsonp",
// what data am I requesting (this will be appended to the URL during request)
data: {
// opensearch - Wiki API action for auto-complete
'action': "opensearch",
     // return the data in the JSON format
'format': "json",
     // what search terms are appended to the opensearch action
'search': query.term
},

// this is the callback function to proceed on a success
success: function (data) {
result(data[1]);
}
});
}

});

As you see (saw in the wiki / SO links above) the JSONP format is the ticket here. Although from what I’ve read there is some risk to using this (hopefully just on my end?).

We’ll take it for now. If you want to gain access to my 8 year old macbook and see all the shitty memes, terrible PreviewShops [:( I’m broke, photoshop is just a dream], dog pictures, and horrendous code. Be my guest.

Cleaner version without comments (might be easier to read…or copy):

search_bar.autocomplete({
source: function(query, result) {
$.ajax({
url: "http://en.wikipedia.org/w/api.php",
dataType: "jsonp",
data: {
'action': "opensearch",
'format': "json",
'search': query.term
},

success: function (data) {
result(data[1]);
}
});
}

});

For comparison’s sake this is how that whole $.ajax request would look in my faithful $.get format:

$.get('http://en.wikipedia.org/w/api.php?format=json&action=opensearch&search='+query.term, success)

And I would have gotten away with it too if it weren’t for you meddling security protocols…

Exhibit A: Terrible PreviewShop

Alright I made it this far. It works (obviously). But what the actually is result and why data[1]? Seems wrong to me (are we skipping the first result data[0]?) and clearly I know better than the fellow writing tutorial articles... So in good fashion of mucking about I tried data[0]. And I didn’t get a damn thing. So then I hit up my good buddy Father Console Log to see what was happening.

success: function (data) {
console.log(data);
}
});

I typed ‘Python’ really fast [dat WPM tho] to keep the log concise and here are the results:

Autocomplete — source: result(data)

Well I found something cool that I can’t find anywhere on the wiki documentation for opensearch or the w3Lessons article. I’m useful! Turns out:

  • data[1] is used because that’s what holds the match strings for results

But!

  • data[2] returns short descriptions (remember that personal goal!)
  • data[3] returns the direct links to each result (aww yeee)

So…I’m done? Ya right. But at least I know I have the basic data to work with. But how do I display it?

Cheap workaround #1:

// Link generator
function link_generator(data){

var i = 0,
title = data[1],
description = data[2],
link = data[3];

for(i; i < data[3].length; i++){
var  list_link = '<a href="'+link[i]+'">'+title[i]+'</a>',
list_description = '<li>'+list_link+'</br>'+description[i]+'</li>';

$('#links').append(list_description);
}
}

called in my success function:

success: function (data) {
result(data[1]);
link_generator(data);
}
Result of cheap workaround #1

Wow that actually worked as expected*.

* don’t forget (I did…) to define your dynamic variables (list_links, list_descriptions) INSIDE your loop!

But the next step is to build this into the results dropdown window. And I use the term ‘window’ very loosely because there is no window and I’m a failure.

Exhibit A: Shitty Memes

Issues to work out:

  1. Unless I type like a caffeinated demon my page gets VERY long VERY quick as it keeps looping for each letter I type / opensearch result it finds and appending onto my #Links unordered list
  2. I have the same number of dropdown windows as an underground prisoner

Solutions approach:

  1. Figure out how to wipe out and replace the results list as each letter is typed / opensearch result is found
  2. Venture into the dreaded world of CSS and make a pretty dropdown window
  3. Come up with better metaphors

Wipe out and replace results list

I’ve got it! So I know of this DOM element method .empty() from my weather app project. I used it to empty out the jumbotron that displayed the weather each time new data was requested (to prevent overlap). I mucked about a bit and found out a few things!

  • result() is some sort of strange [insert technical term here] that let’s you pass whatever you want to it…but it’s not a function? Wat
Exhibit B: Shitty Memes

So I did a thing to my success function:

success: function (data) {
result(data[1], links.empty(), link_generator(data));
}
// links = $('#links')

So what we’re doing here is updating with data[1], emptying the links container, then appending to that container.

Result time:

first letter
first 4 letters — now with 100% less repeating!
issue 1 — solved

I’ll high-five myself because I don’t have any friends. Moving on.

Make a Window

I probably should have read the documentation more thoroughly. jQuery UI comes prebuilt with some great theming — a part of which includes a fancy dropdown window! So that was solved but brought up a more challenging issue…

So my goal here is to have the Title [link] and Description beneath it and all within the search bar dropdown. Essentially moving my cheap workaround from a separate div into the dropdown itself.

However, to accomplish this requires some very significant modifications to the jQuery UI. I tried — I really did try until 5 am last night to get this to work but I simply couldn’t pull it off. Maybe in the future when I understand more about the jUI framework I will revisit and conquer my demons. But for now I am pretty happy with the alternative approach I took.


Phase 2 — Pre-Delivery

I was pretty frustrated last night trying to rewire the dropdown in the jUI framework so I didn’t update as often as I had been prior. That being said here is the current state of the project:

Landing screen [Left] click search icon to produce Search screen [Right]
Once you begin typing the display box appears and random article button fades off

So it looks pretty clean and I put some animations and trickery into the JS:

WikiFinder.js

Now the only problem is the animations are a bit wonky. Admittedly I am not very good with animations. Let’s face it I am not very good at UI / UX in general. I love coding and I love seeing the finished product when I’ve poured hours (days!) into the UI / UX but damn it if it isn’t the most frustrating experience the whole way. It always feels like trying to put an image within text in a Word document. You know what I’m talking about!

Anyways I need to look into fixing these animations to make things a bit smoother. Off to the forums we go…

You can always count on friendly help from StackOverflow. I posted my question and got some good advice:

Don’t overload the slide ups and downs or you will run into the jerky mess I had made. So I made a few adjustments as per the SO advice.

I kept his suggestion of swapping out some of the slides for fades. But the real key here was using the callback parameter of the animations to ensure that they occurred sequentially rather than overlapping and causing the eyesores I was seeing before.

Here is the corrected code with 100% more nesting:

Phase 3 — Delivery!

Well that was a fun experience — nothing like the amount of work that my Weather App took but I definitely learned some new tricks. This project is simple but effective, I’m happy with it. Really wish I had taken the time to document the weather app journey but I’ll be sure to do so for all my future projects. Thanks for reading and stay tuned for my next bit — the Twitch project!

Completed project