1. Recipe-App
.mibile-container
ul>li*3>img+span
i.fas.fa-search
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Recipes App</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" />
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div class="mobile-container">
<header>
<input type="text" id="search-term" />
<button id="search"><i class="fas fa-search"></i></button>
</header>
<div class="fav-container">
<h3>Favorite Meals</h3>
<ul class="fav-meals" id="fav-meals"></ul>
</div>
<div class="meals" id="meals"></div>
</div>
<div class="popup-container hidden" id="meal-popup">
<div class="popup">
<button id="close-popup" class="close-popup">
<i class="fas fa-times"></i>
</button>
<div class="meal-info" id="meal-info"></div>
</div>
</div>
</body>
</html>
style.css
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap');
* {
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background: #ffeeee;
background: -webkit-linear-gradient(to right, #ddefbb, #ffeeee);
background: linear-gradient(to right, #ddefbb, #ffeeee);
display: flex;
align-items: center;
justify-content: center;
margin: 0;
min-height: 100vh;
}
.mobile-container {
width: 400px;
background-color: #fff;
box-shadow: 0 0 10px 2px #3333331a;
border-radius: 3px;
}
img {
max-width: 100%;
}
header {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
header input {
background-color: #eee;
border: none;
border-radius: 3px;
font-family: inherit;
padding: 0.5rem 1rem;
}
header button {
background-color: transparent;
color: #aaa;
border: none;
font-size: 1.5rem;
margin-left: 10px;
}
.fav-container {
background-color: rgb(241, 213, 243);
padding: 0.25rem 1rem;
text-align: center;
}
.fav-meals {
display: flex;
flex-wrap: wrap;
justify-content: center;
list-style-type: none;
padding: 0;
}
.fav-meals li {
cursor: pointer;
position: relative;
margin: 5px;
width: 75px;
}
.fav-meals li img {
border: 2px solid #ffffff;
border-radius: 50%;
box-shadow: 0 0 10px 2px #3333331a;
object-fit: cover;
height: 70px;
width: 70px;
}
.fav-meals li span {
display: inline-block;
font-size: 0.9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 75px;
}
.meal {
border-radius: 3px;
box-shadow: 0 0 10px 2px #3333331a;
margin: 1.5rem;
}
.meal-header {
position: relative;
}
.meal-header .random {
position: absolute;
top: 1rem;
background-color: #fff;
padding: 0.25rem 1rem;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
.meal-header img {
height: 300px;
width: 100%;
}
.meal-body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
}
.meal-body h4 {
margin: 0;
}
.meal-body .fav-btn {
border: none;
background-color: transparent;
font-size: 1.2rem;
cursor: pointer;
color: rgb(190, 190, 190);
}
.meal-body .fav-btn.active {
color: rebeccapurple;
}
.popup-container {
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.popup-container.hidden {
opacity: 0;
pointer-events: none;
}
.popup {
background-color: #fff;
border-radius: 5px;
padding: 0 2rem;
position: relative;
overflow: auto;
max-height: 100vh;
max-width: 600px;
width: 100%;
}
.popup .close-popup {
background-color: transparent;
border: none;
cursor: pointer;
font-size: 1.5rem;
position: absolute;
top: 1rem;
right: 1rem;
}
.meal-info h1 {
text-align: center;
}
script.js
const mealsEl = document.getElementById('meals');
const favoriteContainer = document.getElementById('fav-meals');
const mealPopup = document.getElementById('meal-popup');
const mealInfoEl = document.getElementById('meal-info');
const popupCloseBtn = document.getElementById('close-popup');
const searchTerm = document.getElementById('search-term');
const searchBtn = document.getElementById('search');
getRandomMeal();
fetchFavMeals();
async function getRandomMeal() {
const resp = await fetch('https://www.themealdb.com/api/json/v1/1/random.php');
const respData = await resp.json();
const randomMeal = respData.meals[0];
addMeal(randomMeal, true);
}
async function getMealById(id) {
const resp = await fetch('https://www.themealdb.com/api/json/v1/1/lookup.php?i=' + id);
const respData = await resp.json();
const meal = respData.meals[0];
return meal;
}
async function getMealsBySearch(term) {
const resp = await fetch('https://www.themealdb.com/api/json/v1/1/search.php?s=' + term);
const respData = await resp.json();
const meals = respData.meals;
return meals;
}
function addMeal(mealData, random = false) {
console.log(mealData);
const meal = document.createElement('div');
meal.classList.add('meal');
meal.innerHTML = `
<div class="meal-header">
${
random
? `
<span class="random"> Random Recipe </span>`
: ''
}
<img
src="${mealData.strMealThumb}"
alt="${mealData.strMeal}"
/>
</div>
<div class="meal-body">
<h4>${mealData.strMeal}</h4>
<button class="fav-btn">
<i class="fas fa-heart"></i>
</button>
</div>
`;
const btn = meal.querySelector('.meal-body .fav-btn');
btn.addEventListener('click', () => {
if (btn.classList.contains('active')) {
removeMealLS(mealData.idMeal);
btn.classList.remove('active');
} else {
addMealLS(mealData.idMeal);
btn.classList.add('active');
}
fetchFavMeals();
});
meal.addEventListener('click', () => {
showMealInfo(mealData);
});
mealsEl.appendChild(meal);
}
function addMealLS(mealId) {
const mealIds = getMealsLS();
localStorage.setItem('mealIds', JSON.stringify([...mealIds, mealId]));
}
function removeMealLS(mealId) {
const mealIds = getMealsLS();
localStorage.setItem('mealIds', JSON.stringify(mealIds.filter((id) => id !== mealId)));
}
function getMealsLS() {
const mealIds = JSON.parse(localStorage.getItem('mealIds'));
return mealIds === null ? [] : mealIds;
}
async function fetchFavMeals() {
favoriteContainer.innerHTML = '';
const mealIds = getMealsLS();
for (let i = 0; i < mealIds.length; i++) {
const mealId = mealIds[i];
meal = await getMealById(mealId);
addMealFav(meal);
}
}
function addMealFav(mealData) {
const favMeal = document.createElement('li');
favMeal.innerHTML = `
<img
src="${mealData.strMealThumb}"
alt="${mealData.strMeal}"
/><span>${mealData.strMeal}</span>
<button class="clear"><i class="fas fa-window-close"></i></button>
`;
const btn = favMeal.querySelector('.clear');
btn.addEventListener('click', () => {
removeMealLS(mealData.idMeal);
fetchFavMeals();
});
favMeal.addEventListener('click', () => {
showMealInfo(mealData);
});
favoriteContainer.appendChild(favMeal);
}
function showMealInfo(mealData) {
mealInfoEl.innerHTML = '';
const mealEl = document.createElement('div');
const ingredients = [];
for (let i = 1; i <= 20; i++) {
if (mealData['strIngredient' + i]) {
ingredients.push(`${mealData['strIngredient' + i]} - ${mealData['strMeasure' + i]}`);
} else {
break;
}
}
mealEl.innerHTML = `
<h1>${mealData.strMeal}</h1>
<img
src="${mealData.strMealThumb}"
alt="${mealData.strMeal}"
/>
<p>
${mealData.strInstructions}
</p>
<h3>Ingredients:</h3>
<ul>
${ingredients
.map(
(ing) => `
<li>${ing}</li>
`
)
.join('')}
</ul>
`;
mealInfoEl.appendChild(mealEl);
mealPopup.classList.remove('hidden');
}
searchBtn.addEventListener('click', async () => {
mealsEl.innerHTML = '';
const search = searchTerm.value;
const meals = await getMealsBySearch(search);
if (meals) {
meals.forEach((meal) => {
addMeal(meal);
});
}
});
popupCloseBtn.addEventListener('click', () => {
mealPopup.classList.add('hidden');
});