How to build a Wikipedia Search App with JavaScript (ES6)

One of the best ways to learn a new technology or programming language, and one I always advocate for over anything else, is to build something in that language or technology.

Many people who are in the early stages of their coding journey often ask me about how to take the bits and pieces that they learn and combine them to build a complete app.

I’ve found that there are not so many beginner friendly tutorials on the web and the ones available tend to make a lot of assumptions which often leaves readers frustrated.

I hope to do my part to bridge this gap by writing tutorials on this blog to help beginners tie together concepts in web development and get some apps under their belt.

What we’ll be building

I will take you through how to build a Wikipedia Search App with JavaScript. This is one of Front-End freeCodeCamp projects which I did last year with jQuery.

Here, we won’t be using any third-party libraries. Instead, I will show you how to perform API requests and basic DOM operations with vanilla JavaScript and also demo some ES6 (a.k.a ES2015) features that you may not be familiar with.

These are the user stories for the app:

You can view a complete version of the app here. The code for this project lives on Github.

Prerequisites

This tutorial is intended for beginners to who want to learn how to create simple web apps using JavaScript. You need to understand the basics of HTML, CSS and JavaScript before diving in.

If you use jQuery for DOM manipulation, this tutorial will show you how to replace common jQuery methods with native DOM (Document Object Model) APIs and expose you to some newly introduced ES6 features.

To complete this tutorial, all you need is a modern web browser (such as Firefox or Chrome) and a working Internet connection.

If you get stuck, leave a comment below and I’ll get back to you as soon as I can.

Getting Started

I have created a starting point for this project on JSFiddle. This fiddle already contains all the markup and styling that we need for this tutorial. I’m going to concentrate only on explaining how the JavaScript works.

Start by forking the code to a new fiddle. This will create a new instance of the fiddle for you to work with. As I walk you through this tutorial, make sure to type each step out into your instance. This will help you learn faster.

Fork Starter Fiddle

After each step, you will see a snapshot of the code on this page so you can compare it with your own and see if you make a mistake.

User Story 1 - View a random Wikipedia entry

Let’s start with the easier feature of the two. The user should be able to load a random Wikipedia entry when a button is clicked on our page.

I have already created a button for this purpose, the one with the shuffle icon highlighted in the screenshot below, but it does not do anything yet.

Shuffle Button

The shuffle button is actually a link that wraps around an image which I styled to look like a button in order to fit with the design of the application.

<a href="" class="icon randomIcon">
  <img src="https://image.ibb.co/fR5OX5/random.png" alt="Shuffle Icon">
</a>

To fulfill this user story, there is a special URL we can use: https://en.wikipedia.org/wiki/Special:Random.

If you load that URL in your browser, you will notice that it loads a random Wikipedia entry each time. So all we need to do here is set the href attribute to the special URL so that one can view a random Wikipedia article every time the button is clicked.

Here’s how:

<a href="https://en.wikipedia.org/wiki/Special:Random" target="_blank" rel="noreferrer" class="icon randomIcon">
  <img src="https://image.ibb.co/fR5OX5/random.png" alt="Shuffle Icon">
</a>

Job done! The user story is fulfilled. The keen-eyed will notice the addition of two new attributes to the anchor tag: target="_blank" and rel="noreferrer".

target="_blank" is what makes the link open in a new tab while rel="noreferrer" fixes a security vulneralbility associated with the use of target="_blank".

Here is a snapshot of the completed step. Click the shuffle button a few times to see it in action.

User Story 2 - Search Wikipedia entries

I have broken down this task into three steps:

  1. Grab the search query when the user submits the form.
  2. Send this query to Wikipedia.
  3. Display the results on the page.

Step 1 - Grab the search query when the user submits the form

Our first task is to capture the query when the form is submitted. We can do this by listening for the submit event on the .searchForm element and then capture whatever the user has typed into the input field.

First off, let’s grab a reference to our form element and store it in a variable:

const form = document.querySelector(".searchForm");

We can grab a reference to an element in the DOM using the querySelector() method. It returns the first element that matches the specified selector that we pass to it.

Next, we need to listen for the submit event on the form element so that we can capture the search query when the form is submitted. We will attach the addEventListener method to our form node to achieve this.

form.addEventListener('submit', handleSubmit);

In the above snippet, addEventListener takes two arguments: submit which is the DOM event we want to listen for and handleSubmit which is the function that will run when the event is triggered.

Let’s go ahead and create the handleSubmit function:

function handleSubmit(event) {}

Our function does nothing at the moment, but it takes one parameter which is the event that triggered the execution of the function.

At this point, if you try submitting the form, you will notice that the page reloads. This is because the default behaviour in browsers is for the page to reload when a form is submitted. We can cancel this behaviour by using event.preventDefault() in the handleSubmit function.

function handleSubmit(event) {
  // prevent page from reloading when form is submitted
  event.preventDefault();
}

You can try submitting the form now, the page should not reload anymore.

Now, let’s do something useful with our function. We need to grab a reference to the input field and store its value in a variable.

function handleSubmit(event) {
  // prevent page from reloading when form is submitted
  event.preventDefault();
  // get the value of the input field
  const input = document.querySelector(".searchForm-input").value;
  // remove whitespace from the input
  const searchQuery = input.trim();
  // print `searchQuery` to the console
  console.log(searchQuery);
}

When handleSubmit is summoned, we grab the input field, store its value and then remove whitespace (such as spaces or tabs) from the beginning and end of the raw input using the trim() method before storing the final result in the searchQuery variable. The console.log() statement prints the value of searchQuery to the console.

Open the console in your browser (Ctrl+Shift+J / Cmd+Opt+J in Chrome and Ctrl+Shift+K / Cmd+Opt+K in Firefox). Now type something in the input field and submit the form. If your code is correct, whatever you typed into the search field will be printed to the console.

Print query to console

That concludes the first step for this user story. To recap we:

Let’s move on to step 2 where we will send the query to Wikipedia and handle the response.

Step 2 - Send query to Wikipedia and log response

Our next step involves making an AJAX (Asynchronous JavaScript And XML) request to Wikipedia and handling the response that we get.

To interact with Wikipedia’s API, you do not need to sign up for an API key. But you do need to know which query parameters are compulsory for the specific task you want to perform.

This page contains all the relevant information about the parameters we need to include in our request to perform a full text search in Wikipedia. I have already condensed the relevant parameters for this tutorial into the following URL:

https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&format=json&origin=*&srlimit=20&srsearch=SEARCH_QUERY_GOES_HERE

This URL contains a few parameters I think you should take note of: &format=json specifies that we are expecting a JSON (JavaScript Object Notation) response, &origin=* helps us get around CORS (Cross-Origin Resource Sharing) restrictions, &srlimit=20 specifies how many pages to return (20 in this case) and &srsearch= contains the search query.

Create a new function under handleSubmit:

function fetchResults(searchQuery) {}

fetchResults takes one parameter searchQuery, which is the user’s query we already captured in the previous step.

By the way, return to the handleSubmit function and change console.log(searchQuery) at the end to fetchResults(searchQuery).

function handleSubmit(event) {
  // prevent page from reloading when form is submitted
  event.preventDefault();
  // get the value of the input field
  const input = document.querySelector(".searchForm-input").value;
  // remove whitespace from the input
  const searchQuery = input.trim();
  // call `fetchResults` and pass it the `searchQuery`
  fetchResults(searchQuery);
}

Next, we need to make the actual request to Wikipedia. In jQuery land, you usually use $.getJSON or $.ajax to make AJAX requests in the browser.

However, the browser has a new one built-in now called fetch so that’s what we’ll use here. Before we do that, let me show you how to do string interpolation using template literals.

Before ES6, concatenating strings was a pain. We had to use the addition operator to concatenate strings which was tedious and introduced lots of problems.

var a = "Hello";
console.log(a + " " + "World");  // logs "Hello World"

But we can discard this method forever by taking advantage of ES6 Template Literals.

const a = "Hello";
console.log(`${a} World`);  // logs "Hello World"

All you need to do is enclose the string in backticks (``) and insert any placeholders ${expression} anywhere in the string so that the value of the expression is interpolated into the string and replaces the placeholder.

Modify fetchResults to look like this:

function fetchResults(searchQuery) {
  const endpoint = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&format=json&origin=*&srlimit=20&srsearch=${searchQuery}`;
  console.log(endpoint);
}

In the above snippet, we have ${searchQuery} as our placeholder. When fetchResults() is summoned, whatever is passed in as an argument (the user’s searchQuery) will be interpolated into our endpoint variable as the value of the &srsearch= parameter.

Let’s see this in action. Type some text into the input field, submit the form and open your browser console. You will see that the text you typed has replaced ${searchQuery} in the endpoint which is then logged to the console.

Print endpoint to console

Try this a few times to get the hang of it.

Using Fetch

fetch works differently than $.getJSON, for example, where you can pass in a callback and log the data received from there. Instead, fetch returns what is called a Promise which represents the result of an asynchronous operation.

Instead of passing a callback, you call .then() on it which is how you handle the result of a Promise. We also need to specify what type of data we are expecting (in this case, JSON) before we can access the data and use it.

Change your fetchResults function to look like this:

function fetchResults(query) {
  const endpoint = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&format=json&origin=*&srlimit=20&srsearch=${searchQuery}`;
  
  fetch(endpoint)
  	.then(response => response.json())
  	.then(data => {
  	  console.log(data);
	});
}

fetch takes in the endpoint and specifies that we are expecting a JSON response from Wikipedia. The first .then() expression returns another Promise so we have to call a second .then() on that to handle the JSON data and log it to the console.

Try it out once again. Type into the input field and submit the form. The raw JSON data will be logged to the console.

That concludes step 2 for this user story. To recap we:

Let’s move on to the final step to conclude this tutorial.

Step 3 - Display the results on the page

Our final task is to display the results we get from Wikipedia on the page.

If you examine the contents of the raw JSON data logged to the console in the previous step, you will see that the data object contains a couple of keys with different contents.

The only key that is relevant for this tutorial is search which is an array of objects nested inside the query object. We can access it using dot notation like this data.query.search.

Examine raw JSON data

Modify the fetchResults function to look like this:

function fetchResults(query) {
  const endpoint = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&format=json&origin=*&srlimit=20&srsearch=${searchQuery}`;
  
  fetch(endpoint)
  	.then(response => response.json())
  	.then(data => {
  	  const results = data.query.search;
  	  displayResults(results);
	});
}

The search array obtained from the raw JSON data is assigned to a new variable results and it is passed to another function displayResults.

The next step is to declare the displayResults function under fetchResults:

function displayResults(results) {
  console.log(results);
}

Now, type into the input field and submit the form again. An array of objects should be logged into your console. Each object represents a single result. What we need to do now is display each result on the page.

Print results to console

If you look at the markup for this app, you will see that we have already defined a section where the search results should be displayed.

<main>
  ...
  <section class="searchResults"></section>
</main>

Our task is to loop through each object in the results array and append some markup for each object inside .searchResults.

First, we need to store a reference to .searchResults and make sure all existing child elements are removed before appending any new elements.

function displayResults(results) {
  // Store a reference to `.searchResults`
  const searchResults = document.querySelector(".searchResults");
  // Remove all child elements
  searchResults.innerHTML = "";
}

Next, we will loop over our results array using the forEach method.

function displayResults(results) {
  // Store a reference to `.searchResults`
  const searchResults = document.querySelector(".searchResults");
  // Remove all child elements
  searchResults.innerHTML = "";
  // Loop over results array
  results.forEach(result => { 
  	//result here represents each object in our array
   
  });
}

Now we can access the keys in each object by using the result reference in the body of the callback function. If you check each object you will see keys like title, snippet, timestamp and others. Wikipedia does not provide the url for each result but we need this to be able to link to each page in the search results.

I’ve found that a good way to generate the url is to replace spaces in the title with underscores and tack it to https://en.wikipedia.org/wiki/.

So if we have Usain Bolt as the title, the url becomes https://en.wikipedia.org/wiki/Usain_Bolt.

Another way is to append the title of the result to https://en.wikipedia.org/wiki/ as is, and then encode the URL using the encodeURI method.

So encodeURI('https://en.wikipedia.org/wiki/Usain Bolt') returns https://en.wikipedia.org/wiki/Usain%20Bolt which automatically redirects to the URL with underscores.

function displayResults(results) {
  // Store a reference to `.searchResults`
  const searchResults = document.querySelector(".searchResults");
  // Remove all child elements
  searchResults.innerHTML = "";
  // Loop over results array
  results.forEach(result => {
    const url = encodeURI(`https://en.wikipedia.org/wiki/${result.title}`);
  });
}

Finally, we will append each result to the .searchResults node using another DOM API insertAdjacentHTML.

function displayResults(results) {
  // Store a reference to `.searchResults`
  const searchResults = document.querySelector(".searchResults");
  // Remove all child elements
  searchResults.innerHTML = "";
  
  // Loop over results array
  results.forEach(result => {
   const url = encodeURI(`https://en.wikipedia.org/wiki/${result.title}`);
   
   searchResults.insertAdjacentHTML("beforeend",
      `<div class="resultItem">
        <h3 class="resultItem-title">
          <a href="${url}" target="_blank" rel="noreferrer">${result.title}</a>
        </h3>
        <span class="resultItem-snippet">${result.snippet}</span><br>
        <a href="${url}" class="resultItem-link" target="_blank" rel="noreferrer">${url}</a>
      </div>`
    );
  });
}

insertAdjacentHTML takes two arguments: The position where we want to append the element (beforeend) and a string containing the HTML to insert on the page. beforeend specifies that the HTML in the second argument be appended after the last child of searchResults.

In the second argument, we take advantage of template literals to present the HTML in a more natural and readable manner. We also have several placeholders in the code. These will be replaced of course in the final output.

Try it out. Enter a query into the form and hit the submit button. The results will be appended on the page and each one leads to the corresponding Wikipedia page thus fulfilling the second user story.


That concludes my tutorial. You can improve upon this project by adding your own functionalities and styling to it. Here are three additional features you can implement:

Take a stab at implementing those three features in your application. If you want to see how I tackled those three tasks, view my revised solution to the freeCodeCamp project on GitHub.