Meal Finder App using JavaScript, HTML, and CSS

Creating a Meal Finder App is a fantastic way to learn and apply JavaScript, HTML, and CSS, especially when working with external APIs. In this guide, we will create an application that allows users to search for meals using keywords and fetch random meals from the "TheMealDB" API. This project not only enhances your coding skills but also introduces you to working with APIs, DOM manipulation, and event handling in JavaScript. Let's dive into the specifications and the implementation steps. 

Objective: Allow users to search for meals using keywords or fetch random meals from TheMealDB API and display the results in a user-friendly interface. 

Project Specifications 

Display UI with Form to Search and Button to Generate: Create a user interface with an input field for meal searches and a button to fetch random meal suggestions. 

Connect to API and Get Meals: Utilize TheMealDB API to search for meals based on user input or to fetch a random meal. 

Display Meals in DOM with Image and Hover Effect: Show meal results dynamically in the DOM, including meal images. Implement a hover effect for better user interaction. 

Click on Meal and See the Details: Allow users to click on a meal to view detailed information about it, such as ingredients and preparation steps. 

Click on the Generate Button and Fetch & Display a Random Meal: Implement functionality to let users fetch a random meal by clicking on the designated button. 

Implementation

Create a workspace folder with the name meal-finder-app and within this folder, create three files:
  • index.html - for the app structure.
  • style.css - for styling the app.
  • script.js - for the app's functionality.

1. index.html - HTML Structure 

The HTML markup sets the foundation of the application, featuring a container for the search input and buttons and div elements to display search results and meal details. It's designed to be intuitive and user-friendly.

Open the index.html file and add the following HTML code to it:

<!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://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Meal Finder</title>
  </head>
  <body>
    <div class="container">
      <h1>Meal Finder</h1>
      <div class="flex">
        <form class="flex" id="submit">
          <input
            type="text"
            id="search"
            placeholder="Search for meals or keywords"
          />
          <button class="search-btn" type="submit">
            <i class="fas fa-search"></i>
          </button>
        </form>
        <button class="random-btn" id="random">
          <i class="fas fa-random"></i>
        </button>
      </div>

      <div id="result-heading"></div>
      <div id="meals" class="meals"></div>
      <div id="single-meal"></div>
    </div>

    <script src="script.js"></script>
  </body>
</html>

2. style.css - CSS Styling 

CSS styles are applied to enhance the application's visual appeal. The use of grid layouts for meal displays, hover effects for meal items, and responsive design principles ensure that the app is accessible across various devices.

Let's open the style.css file and add the following CSS to to it:
* {
  box-sizing: border-box;
}

body {
  background: #2d2013;
  color: #fff;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
  margin: 0;
}

.container {
  margin: auto;
  max-width: 800px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.flex {
  display: flex;
}

input,
button {
  border: 1px solid #dedede;
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
  font-size: 14px;
  padding: 8px 10px;
  margin: 0;
}

input[type='text'] {
  width: 300px;
}

.search-btn {
  cursor: pointer;
  border-left: 0;
  border-radius: 0;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.random-btn {
  cursor: pointer;
  margin-left: 10px;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.meals {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 20px;
  margin-top: 20px;
}

.meal {
  cursor: pointer;
  position: relative;
  height: 180px;
  width: 180px;
  text-align: center;
}

.meal img {
  width: 100%;
  height: 100%;
  border: 4px #fff solid;
  border-radius: 2px;
}

.meal-info {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.2s ease-in;
  opacity: 0;
}

.meal:hover .meal-info {
  opacity: 1;
}

.single-meal {
  margin: 30px auto;
  width: 70%;
}

.single-meal img {
  width: 300px;
  margin: 15px;
  border: 4px #fff solid;
  border-radius: 2px;
}

.single-meal-info {
  margin: 20px;
  padding: 10px;
  border: 2px #e09850 dashed;
  border-radius: 5px;
}

.single-meal p {
  margin: 0;
  letter-spacing: 0.5px;
  line-height: 1.5;
}

.single-meal ul {
  padding-left: 0;
  list-style-type: none;
}

.single-meal ul li {
  border: 1px solid #ededed;
  border-radius: 5px;
  background-color: #fff;
  display: inline-block;
  color: #2d2013;
  font-size: 12px;
  font-weight: bold;
  padding: 5px;
  margin: 0 5px 5px 0;
}

@media (max-width: 800px) {
  .meals {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 700px) {
  .meals {
    grid-template-columns: repeat(2, 1fr);
  }

  .meal {
    height: 200px;
    width: 200px;
  }
}
@media (max-width: 500px) {
  input[type='text'] {
    width: 100%;
  }

  .meals {
    grid-template-columns: 1fr;
  }

  .meal {
    height: 300px;
    width: 300px;
  }
}

3. script.js - JavaScript Logic 

The JavaScript file is crucial for bringing the application to life. It handles user interactions, API requests, and DOM updates. The searchMeal function fetches meals based on user input, while getRandomMeal retrieves a random meal from the API. The addMealToDOM function dynamically displays the meal information in the HTML document. Event listeners are used to handle form submissions and button clicks, ensuring a smooth and interactive user experience.

Let's open the script.js file and add the following JavaScript code to it:  
const search = document.getElementById('search'),
  submit = document.getElementById('submit'),
  random = document.getElementById('random'),
  mealsEl = document.getElementById('meals'),
  resultHeading = document.getElementById('result-heading'),
  single_mealEl = document.getElementById('single-meal');

// Search meal and fetch from API
function searchMeal(e) {
  e.preventDefault();

  // Clear single meal
  single_mealEl.innerHTML = '';

  // Get search term
  const term = search.value;

  // Check for empty
  if (term.trim()) {
    fetch(`https://www.themealdb.com/api/json/v1/1/search.php?s=${term}`)
      .then(res => res.json())
      .then(data => {
        console.log(data);
        resultHeading.innerHTML = `<h2>Search results for '${term}':</h2>`;

        if (data.meals === null) {
          resultHeading.innerHTML = `<p>There are no search results. Try again!<p>`;
        } else {
          mealsEl.innerHTML = data.meals
            .map(
              meal => `
            <div class="meal">
              <img src="${meal.strMealThumb}" alt="${meal.strMeal}" />
              <div class="meal-info" data-mealID="${meal.idMeal}">
                <h3>${meal.strMeal}</h3>
              </div>
            </div>
          `
            )
            .join('');
        }
      });
    // Clear search text
    search.value = '';
  } else {
    alert('Please enter a search term');
  }
}

// Fetch meal by ID
function getMealById(mealID) {
  fetch(`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealID}`)
    .then(res => res.json())
    .then(data => {
      const meal = data.meals[0];

      addMealToDOM(meal);
    });
}

// Fetch random meal from API
function getRandomMeal() {
  // Clear meals and heading
  mealsEl.innerHTML = '';
  resultHeading.innerHTML = '';

  fetch(`https://www.themealdb.com/api/json/v1/1/random.php`)
    .then(res => res.json())
    .then(data => {
      const meal = data.meals[0];

      addMealToDOM(meal);
    });
}

// Add meal to DOM
function addMealToDOM(meal) {
  const ingredients = [];

  for (let i = 1; i <= 20; i++) {
    if (meal[`strIngredient${i}`]) {
      ingredients.push(
        `${meal[`strIngredient${i}`]} - ${meal[`strMeasure${i}`]}`
      );
    } else {
      break;
    }
  }

  single_mealEl.innerHTML = `
    <div class="single-meal">
      <h1>${meal.strMeal}</h1>
      <img src="${meal.strMealThumb}" alt="${meal.strMeal}" />
      <div class="single-meal-info">
        ${meal.strCategory ? `<p>${meal.strCategory}</p>` : ''}
        ${meal.strArea ? `<p>${meal.strArea}</p>` : ''}
      </div>
      <div class="main">
        <p>${meal.strInstructions}</p>
        <h2>Ingredients</h2>
        <ul>
          ${ingredients.map(ing => `<li>${ing}</li>`).join('')}
        </ul>
      </div>
    </div>
  `;
}

// Event listeners
submit.addEventListener('submit', searchMeal);
random.addEventListener('click', getRandomMeal);

mealsEl.addEventListener('click', e => {
  const mealInfo = e.composedPath().find(item => {
    if (item.classList) {
      return item.classList.contains('meal-info');
    } else {
      return false;
    }
  });

  if (mealInfo) {
    const mealID = mealInfo.getAttribute('data-mealid');
    getMealById(mealID);
  }
});

Testing - Open index.html in Browser 

Let's open the index.html file in the browser, and you will see the following screen. Next, search the meals with the keyword "chicken", and you will see a list of chicken meals, as shown in the screenshot below:
Meal Finder App using JavaScript, HTML, and CSS

Conclusion 

Building a Meal Finder App using JavaScript, HTML, and CSS is a rewarding project that demonstrates the power of API integration and dynamic web content. Through this project, developers can gain practical experience in creating interactive web applications, handling API data, and refining their programming skills. 

By following the project specifications and understanding the implementation details, you can create a functional and visually appealing Meal Finder App that provides users with delicious meal options at their fingertips.

Comments