Simple JS Projects: Project 1: Finddit - A Reddit Search App

Let's learn to create a simple reddit search app using Reddit API, ES6, Fetch and Parcel.User can search subreddit by typing in the input field and all the reddit/subreddit will be fetched from the reddit server.

Designing the HomePage

The homepage is fairly simple. It contains a navbar and a form with 3 input fields one for entering the search term and other two for sorting by relevance or latest reddit posts. The last element is the select option to limit our search results. I have used Bootstrap 4 for styling.

Code for homepage is given below

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
      integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
      crossorigin="anonymous"
    />
    <title>Reddit Search</title>
  </head>
  <body>
    <nav class="navbar navbar-dark bg-primary mb-3">
      <div class="container">
        <span class="navbar-brand">Finddit</span>
      </div>
    </nav>
    <div id="search-container" class="container">
      <div id="search" class="card card-body bg-light mb-2">
        <h4>Search Reddit</h4>
        <form id="search-form">
          <div class="form-group">
            <input
              type="text"
              id="search-input"
              class="form-control mb-3"
              placeholder="Search Term..."
            />
          </div>
          <div class="form-check form-check-inline">
            Sort By:
            <input
              type="radio"
              class="form-check-input ml-2"
              name="sortby"
              value="relevance"
              checked
            />
            <label class="form-check-label">
              Relevance
            </label>
          </div>
          <div class="form-check form-check-inline">
            <input
              type="radio"
              class="form-check-input ml-2"
              name="sortby"
              value="relevance"
            />
            <label class="form-check-label">
              Latest
            </label>
          </div>
          <h5 class="mt-2">Limit:</h5>
          <div class="form-group">
            <select name="limit" id="limit" class="form-control">
              <option value="5">5</option>
              <option value="10">10</option>
              <option value="25" selected>25</option>
              <option value="50">50</option>
              <option value="100">100</option>
            </select>
          </div>
          <button type="submit" class="btn btn-dark btn-block mt-4">
            Search
          </button>
        </form>
      </div>
      <div id="results"></div>
    </div>
    <script src="index.js"></script>
  </body>
</html>

Let’s play with Reddit API

Reddit API has a lot of functionalities to offer but the one we are interested for this project is the search endpoint with q: a string no longer than 512 characters, limit: the maximum number of items desired (default: 25, maximum: 100) and sort: one of (relevance, hot, top, new, comments) I have created a separate js file with the name redditapi.js to make Reddit API calls using fetch() and export it as default. The search function takes 3 parameters(searchTerm, searchLimit, sortBy) and then we chain the promises to tap onto the data.collection.data property which return the actual reddit/subreddit data we need for this app.

Code for fetching data using Reddit API

export default {
  search: function (searchTerm, searchLimit, sortBy) {
    return fetch(`http://www.reddit.com/search.json?q=${searchTerm}
    &sort=${sortBy}&limit=${searchLimit}`)
      .then((res) => res.json())
      .then((data) => data.data.children.map((data) => data.data))
      .catch((err) => console.log(err));
  },
};

Adding functionality with JS

First of all we need to import the default object from the redditapi.js file. And then add an event listener to the form and store the searchTerm, sortBy, searchLimit to search for the specific word, to sort that word according to relevance or latest posts and to limit our search results respectively. You can add a bootstrap alert message to prompt the user when the search term wasn’t entered. We need to create a div to display the search results and add all the following created tags and append them via innerHTML. We then need to create div with className of card-columns:Bootstrap’s cards provide a flexible and extensible content container with multiple variants and options. We need to call the search function and attach a promise since we are making an asynchronous call. We then have to run a forEach loop for every post and render post image, title, selftext, url, subreddit and score. I have added a default image url in case if the reddit/subreddit post don’t have an image to display. I have implemented a truncate function to truncate the text if the selftext property is longer than 100 words. You can implement your own custom truncate function or looks for various truncate function in StackOverflow. I have defined a showMessage function which takes a message to be displayed and the className which is a BootStrap alert message to display the alert notification and have set the timeout to be 3000ns.

Code for index.js file

import reddit from "./redditapi";

const searchForm = document.getElementById("search-form");
const searchInput = document.getElementById("search-input");

// Form Event Listener
searchForm.addEventListener("submit", (e) => {
  // Get search term
  const searchTerm = searchInput.value;
  // Get sort
  const sortBy = document.querySelector('input[name="sortby"]:checked').value;
  // Get limit
  const searchLimit = document.getElementById("limit").value;
  e.preventDefault();

  // Check input
  if (searchTerm === "") {
    //show message
    showMessage("Please add a search term", "alert-danger");
  }

  // Clear input
  searchInput.value = "";

  // Search Reddit
  reddit.search(searchTerm, searchLimit, sortBy).then((results) => {
    let output = `<div class="card-columns">`;
    // Loop through posts
    results.forEach((post) => {
      // Check for image
      let image = post.preview
        ? post.preview.images[0].source.url
        : "https://blog.en.uptodown.com/files/2019/02/reddit-featured.jpg";
      output += `<div class="card">
        <img class="card-img-top" src="${image}" alt="Card image cap">
        <div class="card-body">
          <h5 class="card-title">${post.title}</h5>
          <p class="card-text">${truncateText(post.selftext, 100)}</p>
          <a href="${
            post.url
          }" target="_blank" class="btn btn-primary">Read More</a>
          <hr>
          <span class="badge badge-secondary">Subreddit: ${
            post.subreddit
          } </span>
          <span class="badge badge-dark">Subreddit: ${post.score} </span>
        </div>
      </div>`;
    });
    output += `</div>`;
    document.getElementById("results").innerHTML = output;
  });
});

// Show message
function showMessage(message, className) {
  // Create div
  const div = document.createElement("div");
  div.className = `alert ${className}`;
  // Add text
  div.appendChild(document.createTextNode(message));
  // Get parent
  const searchContainer = document.getElementById("search-container");
  // Get search
  const search = document.getElementById("search");
  // insert Message
  searchContainer.insertBefore(div, search);
  // Timeout alert
  setTimeout(() => {
    document.querySelector(".alert").remove();
  }, 3000);
}

// Truncate text
function truncateText(text, limit) {
  const shortened = text.indexOf(" ", limit);
  if (shortened == -1) return text;
  return text.substring(0, shortened);
}

To run this project locally we will be using Parcel bundler.

To install Parcel run the below command

$npm install -g parcel-bundler

To run the project

$parcel index.html

When we type the search term in the input field and click on Search we get the results.

finddit-results

Hope you have enjoyed the article! Happy Coding

blog