본문 바로가기

기타/글로컬청년취업사관학교

[글로컬청년취업사관학교][TIL] 240712

메뉴판(부제 : 맛집 리스트)

 

요구사항

- 음식의 이름,설명,가격이 포함된 메뉴판 만들기

- 카테고리 버튼을 두고 선택시에 해당 카테고리의 음식만 보여주기 

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Menu</title>
    <!-- font-awesome -->
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"
    />
    <!-- styles -->
    <link rel="stylesheet" href="styles.css" />
</head>
<body>
    <section>
        <div>
            <h1>Our Menu</h1>
            <hr>
            <div class="category-container"></div>
        </div>
        <div class="menu-container"></div>
    </section>
    <script src="app.js"> </script>
</body>
</html>

 

 

app.js

const menu = [
  {
    id: 1,
    title: "buttermilk pancakes",
    category: "breakfast",
    price: 15.99,
    img: "./images/menu1.jpeg",
    desc: `I'm baby woke mlkshk wolf bitters live-edge blue bottle, hammock freegan copper mug whatever cold-pressed `,
  },
  {
    id: 2,
    title: "diner double",
    category: "lunch",
    price: 13.99,
    img: "./images/menu2.jpeg",
    desc: `vaporware iPhone mumblecore selvage raw denim slow-carb leggings gochujang helvetica man braid jianbing. Marfa thundercats `,
  },
  {
    id: 3,
    title: "godzilla milkshake",
    category: "shakes",
    price: 6.99,
    img: "./images/menu3.jpeg",
    desc: `ombucha chillwave fanny pack 3 wolf moon street art photo booth before they sold out organic viral.`,
  },
  {
    id: 4,
    title: "country delight",
    category: "breakfast",
    price: 20.99,
    img: "./images/menu4.jpeg",
    desc: `Shabby chic keffiyeh neutra snackwave pork belly shoreditch. Prism austin mlkshk truffaut, `,
  },
  {
    id: 5,
    title: "egg attack",
    category: "lunch",
    price: 22.99,
    img: "./images/menu5.jpeg",
    desc: `franzen vegan pabst bicycle rights kickstarter pinterest meditation farm-to-table 90's pop-up `,
  },
  {
    id: 6,
    title: "oreo dream",
    category: "shakes",
    price: 18.99,
    img: "./images/menu6.jpeg",
    desc: `Portland chicharrones ethical edison bulb, palo santo craft beer chia heirloom iPhone everyday`,
  },
  {
    id: 7,
    title: "bacon overflow",
    category: "breakfast",
    price: 8.99,
    img: "./images/menu7.jpeg",
    desc: `carry jianbing normcore freegan. Viral single-origin coffee live-edge, pork belly cloud bread iceland put a bird `,
  },
  {
    id: 8,
    title: "american classic",
    category: "lunch",
    price: 12.99,
    img: "./images/menu8.jpeg",
    desc: `on it tumblr kickstarter thundercats migas everyday carry squid palo santo leggings. Food truck truffaut  `,
  },
  {
    id: 9,
    title: "quarantine buddy",
    category: "shakes",
    price: 16.99,
    img: "./images/menu9.jpeg",
    desc: `skateboard fam synth authentic semiotics. Live-edge lyft af, edison bulb yuccie crucifix microdosing.`,
  },
  {
    id: 10,
    title: "bison steak",
    category: "dinner",
    price: 22.99,
    img: "./images/menu10.jpeg",
    desc: `skateboard fam synth authentic semiotics. Live-edge lyft af, edison bulb yuccie crucifix microdosing.`,
  },
];

let selectedCategory = "all";

function createCategoryBtn(category) {
  const $button = document.createElement("button");
  $button.classList.add("filter-btn");
  $button.textContent = category;
  return $button;
}

const $categoryContainer = document.querySelector(".category-container");

const category = menu.reduce(
  (values, item) => {
    console.log(item.id, values, item.category);
    if (!values.includes(item.category)) {
      values.push(item.category);
    }
    return values;
  },
  ["all"]
);

category.forEach((category) => {
  const $btn = createCategoryBtn(category);
  $btn.addEventListener("click", (event) => {
    selectedCategory = event.currentTarget.textContent;
    displayMenu();
  });
  $categoryContainer.append($btn);
});

function createMenuItem(item) {
  const { id, title, category, price, img, desc } = item;
  const $article = document.createElement("article");
  $article.classList.add("menu-item");

  const $image = document.createElement("img");
  $image.setAttribute("src", img);
  $article.appendChild($image);

  const $info = document.createElement("div");
  $info.classList.add("menu-info");

  const $title = document.createElement("h2");
  $title.textContent = title;
  $info.appendChild($title);

  const $price = document.createElement("h2");
  $price.textContent = price;
  $info.appendChild($price);

  const $desc = document.createElement("p");
  $desc.textContent = desc;
  $info.appendChild($desc);

  $article.appendChild($info);

  return $article;
}

function clearDisplayMenu() {
  const items = document.querySelectorAll(".menu-item");
  console.log(items);
  if (!items.length) {
    console.log("There is no items!");
    return;
  }
  items.forEach((item) => $menuContainer.removeChild(item));
}

function displayMenu() {
  clearDisplayMenu();

  let displayItem = menu;
  if (selectedCategory !== "all") {
    displayItem = menu.filter((item) => item.category === selectedCategory);
    console.log(displayItem);
  }
  displayItem.forEach((item) => {
    const $item = createMenuItem(item);
    $menuContainer.appendChild($item);
  });
}

const $menuContainer = document.querySelector(".menu-container");
displayMenu();

 

 

style.css

img:not(.logo) {
  width: 100%;
}
img {
  display: block;
}

/*  global classes */

.btn {
  text-transform: uppercase;
  background: transparent;
  color: var(--clr-black);
  padding: 0.375rem 0.75rem;
  letter-spacing: var(--spacing);
  display: inline-block;
  transition: var(--transition);
  font-size: 0.875rem;
  border: 2px solid var(--clr-black);
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  border-radius: var(--radius);
}

.btn:hover {
  color: var(--clr-white);
  background: var(--clr-black);
}

/* section */
.section {
  padding: 5rem 0;
}

main {
  min-height: 100vh;
  display: grid;
  place-items: center;
}

/*
=============== 
Menu
===============
*/

.menu {
  padding: 5rem 0;
}

h1 {
  text-align: center;
  margin-bottom: 2rem;
}

hr {
  width: 5rem;
  height: 0.25rem;
  background: var(--clr-gold);
  margin-left: auto;
  margin-right: auto;
}

.category-container {
  margin-bottom: 4rem;
  display: flex;
  justify-content: center;
}

.filter-btn {
  background: transparent;
  border-color: var(--clr-gold);
  font-size: 1rem;
  text-transform: capitalize;
  margin: 0 0.5rem;
  letter-spacing: 1px;
  border-radius: var(--radius);
  padding: 0.375rem 0.75rem;
  color: var(--clr-gold);
  cursor: pointer;
  transition: var(--transition);
}

.filter-btn:hover {
  background: var(--clr-gold);
  color: var(--clr-white);
}

.menu-container {
  width: 90vw;
  margin: 0 auto;
  max-width: 1170px;
  display: grid;
  gap: 3rem 2rem;
  justify-items: center;
}

.menu-item {
  display: grid;
  gap: 1rem 2rem;
  max-width: 25rem;
}

img {
  object-fit: cover;
  height: 200px;
  border: 0.25rem solid var(--clr-gold);
  border-radius: var(--radius);
}

h2 {
  display: flex;
  justify-content: space-between;
  border-bottom: 0.5px dotted var(--clr-grey-5);
}

.item-info h4 {
  margin-bottom: 0.5rem;
}

.price {
  color: var(--clr-gold);
}

p {
  margin-bottom: 0;
  padding-top: 1rem;
}

@media screen and (min-width: 768px) {
  .menu-item {
    grid-template-columns: 225px 1fr;
    gap: 0 1.25rem;
    max-width: 40rem;
  }

  .photo {
    height: 175px;
  }
}

@media screen and (min-width: 1200px) {
  .section-center {
    width: 95vw;
    grid-template-columns: 1fr 1fr;
  }

  .photo {
    height: 150px;
  }
}

 

 

 

 

 

칵테일 소개 페이지 

 

요구사항

- API를 통해 칵테일에 대한 정보를 받아오기

- 받아온 정보 기반으로 화면에 그려죽;

- 검색을 통해 필터링이 가능하도록 만들어주기 

 

const URL = "https://www.thecocktaildb.com/api/json/v1/1/search.php?s=a";

//1. fetch 사용
function fetchDrinks() {
fetch(URL)
 .then((response) => {
     return response.json();
  })
 .then((data) => {
   console.log(data);
  })
   .catch(error=>console.error(error));
}

//2.async await 사용
async function fetchDrinks() {
  try {
    const response = await fetch(URL);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
  console.log("finished!");
}

fetchDrinks();

 

 

 

 

 

function fetchDrinks() {
    fetch(URL)
        .then((response) => {
            return response.json();
        })
        .then((data) => {
            // console.log(data.drinks[0]);
            const $container = document.querySelector('.item-container')
            const $img = document.createElement('img');
            $img.setAttribute('src', data.drinks[0].strDrinkThumb);
            $container.appendChild($img);
            // return fetch(data.drinks[0].strDrinkThumb);
        })
        .catch(error => console.error(error));

    console.log('finish');
    // const data = response.json();
    // console.log(data);
}

 

 

Array 고차함수 

 

고차 함수 

- 함수를 파라미터로 전달받거나 연산의 결과로 반환해주는 메서드 

- 함수형 프로그래밍의 핵심 

 

forEach()

- for문을 대체하는 고차함수, 반복문을 추상화하여 구현된 메서드이고 내부에서 주어진 배열을 순회하면서 연산을 수행 

 

const numArr = [1,2,3,4,5];
int total = 0;

numArr.forEach((item) => {
	total+=item;
});

console.log(total); //15

 

 

map()

 

- forEach와 같이 순회하면서, 콜백함수에서의 실행결과를 리턴한 값으로 이루어진 배열을 만들어 반환 

const numArr = [1,2,3,4,5];
const numMaparr = numArr.map((item) => {
	return (item%2==0)? 'even' : 'odd';
});

console.log(numArr); //['odd','even','odd','even','odd']

 

 

find()

 

- indexOf()가 찾고자 하는 인덱스 

- include()가 찾고자 하는 값을 Bool

- find()는 찾고자 하는 값을 그대로 반환 

 

 

const numArr = [1,3,3,5,7];
const objectArr = [
	{ name: 'Harry', age: 20 },
 	{ name: 'Kim', age: 30 },
	 { name: 'Steve', age: 40 }
];

console.log(objectArr.find(item => {
	return  item.age === 20; //해당조건에 부합하면 item 값 반환 
}); // {name: "Harry", age: 20}

 

 

findIndex() 

 

- 배열 메소드 indexOf()의 콜백함수 버전

- 고차함수 find()의 리턴값이 인덱스인 버전 

const objectArr = [
	{ name: 'Harry', age: 20 },
 	{ name: 'Kim', age: 30 },
 	{ name: 'Steve', age: 40 }
];

console.log(objectArr.findIndex(item => {
	return item.age === 20 //해당조건에 부합하면 item의 인덱스 반환 
}); //0

console.log(objectArr.findIndex(item => item.name === 'Kim')); //1

 

 

filter()

 

- 주어진 배열을 순회하면서 콜백 함수의 반환값이 true에 해당하는 요소로만 구성된 새로운 배열을 생성하여 반환

 

const numberArr = [1, 2, 3, 4, 5];
const numberFilterArr = numberArr.filter((item) => {
 return item % 2 === 0; // 해당조건에 부합으면 item을 넣어 배열 반환
});
console.log(numberFilterArr); // [2, 4]

 

 

reduce()

 

- 콜백 함수의 실행된 반환값을 전달받아 연산의 결과값이 반환 

- forEach, map, filter 기능을 reduce로 모두 구현해서 쓸순 있어 고차함수의 부모라고 불린다.

 

const numberArr = [1, 2, 3, 4, 5];
const sum = numberArr.reduce((previousValue, currentValue, currentIndex, thisArray) => {
 console.log('Current Index: ' + currentIndex + ' / Previous Value: ' + previousValue + ' / C
 
 return previousValue + currentValue; // 연산한 결과값을 누산기previousValue에 넣어 최종값을 얻는다.
}, 0);
console.log('Sum: ' + sum);
/*
Current Index: 0 / Previous Value: 0 / Current Value: 1
Current Index: 1 / Previous Value: 1 / Current Value: 2
Current Index: 2 / Previous Value: 3 / Current Value: 3
Current Index: 3 / Previous Value: 6 / Current Value: 4
Current Index: 4 / Previous Value: 10 / Current Value: 5
Sum: 15
*/
💡 initalValue의

 

 

sort() 

 

- 배열 정렬. 원 배열이 정렬됨. 콜백 함수를 통해 배열의 원소들을 어느 기준으로 정렬할지 지정 

 

arr.sort(function(a,b) {
	if(a>b) return 1;
    if(a==b) return 0;
    if(a<b) return -1;
});

 

const arr = [
 {name: 'banana', price: 3000},
 {name: 'apple', price: 1000},
 {name: 'orange', price: 500}
];
arr.sort(function(a, b) {
 return a.price - b.price; // price 숫자값을 기준으로 정렬
});
/*
{"name":"orange","price":500}
{"name":"apple","price":1000}
{"name":"banana","price":3000}
*/

 

some() 

 

- 배열 메소드 include()의 콜백함수 버전 

- include는 값이 있냐에 따른 bool이면, some은 함수 로직에 따른 bool

- 배열의 요소들을 주어진 함수(조건)을 통과하는데 한 개라도 통과되면 true, 아닐때에는 false

- 빈 배열로 함수(조건)을 토과하면 무조건 false를 출력. 이와 같이 some이라는 함수(조건)에 부합한 개수가 some이면 true라는 뜻에서 비롯됨 

 

const array = [1, 3, 5];
// 짝수인지 체크
const result = array.some((currentValue) => {
return currentValue % 2 === 0;
})
console.log(result); // 리턴 값 : false
// 그 이유는 array의 3개의 요소 모두 2로 나눌때 나머지가 0이 아니기 때문이다.
// 하나라도 부합한 조건에 맞으면 true, 모두 부합하지 않으면 false

 

const array2 = [1, 2, 3, 5];
const result2 = array2.some((currentValue) => {
return currentValue % 2 === 0;
})
console.log(result2); // 리턴 값 : true
// 그 이유는 array의 4개의 요소 모두 2로 나눌때 나머지가 0인 요소가 하나라도 있기 때문이다.
// 하나라도 부합한 조건에 맞으면 true, 모두 부합하지 않으면 false

 

 

every()

 

- some() 의 반대 버전 

- 배열안의 모든 요소가 주어진 함수(조건)을 모두 통과하면 true, 한 요소라도 통과하지 못하면 false

- 빈 배열을 함수에 적용시키면 무조건 true를 반환 

- every이라는 이름은, 함수(조건)에 따라 함수(조건)에 부합한 개수가 every이면 true라는 뜻에서 비롯됨 

 

const array = [1, 30, 39, 29, 13];
const result = array.every((currentValue) => {
return currentValue < 40;
})
console.log(result); // 리턴 값 : true
// 그 이유는 array의 모든 요소가 40보다 작기 때문이다.
// 하나라도 부합한 조건에 맞지 안으면 false, 모두 부합하면 true
// -----------------------------------------------
const array2 = [1, 30, 39, 29, 100, 13];
const result2 = array2.every((currentValue) => {
return currentValue < 40;
})
console.log(result2); // 리턴 값 : false
// 그 이유는 array의 1개의 요소 100이 40보다 크기 때문이다.
// 하나라도 부합한 조건에 맞지 안으면 false, 모두 부합하면 true