Vanilla JavaScript Clone Coding

아현·2021년 8월 21일
0

JavaScript

목록 보기
2/3

1. Recipe-App




//emmet


.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() {
	// clean the container
	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) {
	// clean it up
	mealInfoEl.innerHTML = '';

	// update the Meal info
	const mealEl = document.createElement('div');

	const ingredients = [];

	// get ingredients and measures
	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);

	// show the popup
	mealPopup.classList.remove('hidden');
}

searchBtn.addEventListener('click', async () => {
	// clean container
	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');
});



profile
For the sake of someone who studies computer science

0개의 댓글