Project.log

[HTML, CSS, JS] 음식메뉴판 만들기 본문

Programming/미니프로젝트

[HTML, CSS, JS] 음식메뉴판 만들기

jinuk_ 2023. 9. 8. 00:45
728x90
반응형

자주가는 피셔맨스키친 메뉴판 만들어보기

목차

  • 만들게 된 계기
  • 쓰인 기술
  • 설명
  • 링크

만들게 된 계기

패스트캠퍼스X야놀자 프론트엔드 부트캠프 과정중에 한주 TS와 React실시간 강의 과정이 강사님 사정으로 사라져서

무엇을 하면 좋을까 하다가 JS(Dom)을 적극활용해서 뭔가를 만들어보면 실력이 늘지 않을까 싶어서 시작하게 되었다.

동네 근처에 자주가는 음식점인 피셔맨스키친에 메뉴판을 만들어보면 어떨까 해서 만들게 되었습니다.

(❖그림과 내용이 일치하지 않는 것도 있습니다.)

 

 

쓰인 기술

HTML5(div, section, header ...)

CSS3(Reset.css, Flex, Grid, @media query ...)

JS(Array, Object, forEach(), DOMContentLoaded, map, reduce, filter, innerHTML, includes method ...)

 

 

설명

JS미니프로젝트로써 HTML과 CSS설명은 생략하고 전체코드로 대신하겠습니다.

 

HTML

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>피셔맨스키친</title>
    <link rel="shortcut icon" href="img/fav.ico" />
    <link rel="stylesheet" href="css/reset.css" />
    <link rel="stylesheet" href="css/ui.css" />
  </head>
  <body>
    <div class="menu">
      <header class="title">
        <h2>피셔맨스키친</h2>
        <div class="underline"></div>
      </header>

      <section class="btn-container">
        <button>전체메뉴</button>
        <button>에피타이저</button>
        <button>파스타</button>
        <button>피자</button>
        <button>리조또</button>
        <button>스테이크</button>
        <button>음료</button>
      </section>

      <section class="main-container">
        <div class="menu-item">
          <img src="img/APPETIZER1.jpeg" alt="#" class="photo" />
          <div class="item-info">
            <div>
              <h4>등심샐러드</h4>
              <h4 class="price">22,000원</h4>
            </div>
            <p class="item-text">
              Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fugiat
              quidem voluptates earum, unde animi sit repudiandae suscipit
              laborum magnam culpa eum soluta? Repudiandae nemo iusto est dolore
              ratione? Excepturi, eius.
            </p>
          </div>
        </div>

        <div class="menu-item">
          <img src="img/APPETIZER1.jpeg" alt="#" class="photo" />
          <div class="item-info">
            <div>
              <h4>등심샐러드</h4>
              <h4 class="price">22,000원</h4>
            </div>
            <p class="item-text">
              Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fugiat
              quidem voluptates earum, unde animi sit repudiandae suscipit
              laborum magnam culpa eum soluta? Repudiandae nemo iusto est dolore
              ratione? Excepturi, eius.
            </p>
          </div>
        </div>

        <div class="menu-item">
          <img src="img/APPETIZER1.jpeg" alt="#" class="photo" />
          <div class="item-info">
            <div>
              <h4>등심샐러드</h4>
              <h4 class="price">22,000원</h4>
            </div>
            <p class="item-text">
              Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fugiat
              quidem voluptates earum, unde animi sit repudiandae suscipit
              laborum magnam culpa eum soluta? Repudiandae nemo iusto est dolore
              ratione? Excepturi, eius.
            </p>
          </div>
        </div>
      </section>
    </div>
    <script src="js/index.js"></script>
  </body>
</html>

CSS

body {
  background-color: whitesmoke;
}

.menu {
  padding: 4rem 0;
}

.title {
  text-align: center;
  margin-bottom: 30px;
}

.title h2 {
  font-weight: bold;
  font-size: 40px;
  margin-bottom: 14px;
  color: rgb(1, 88, 121);
}

.underline {
  width: 20%;
  background-color: #c59d5f;
  height: 4px;
  margin: 0 auto;
}

.btn-container {
  display: flex;
  justify-content: center;
  margin: 10px 0 50px 0;
}

.btn-container button {
  margin: 0 4px;
  padding: 6px 14px;
  color: rgb(1, 88, 121);
  background: none;
  border-radius: 8px;
  border-color: #c59d5f;
  font-weight: bold;
  font-size: 10px;
}

.btn-container button:hover {
  cursor: pointer;
  background-color: #c59d5f;
  color: #fff;
}

.main-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;
}

.photo {
  object-fit: cover;
  height: 200px;
  border: 0.25rem solid #c59d5f;
  border-radius: 0.5rem;
}

.item-info div {
  display: flex;
  justify-content: space-between;
  border-bottom: 0.5px dotted gray;
}
.item-info h4 {
  margin-bottom: 0.5rem;
}
.price {
  color: #c59d5f;
}
.item-text {
  margin-bottom: 0;
  padding-top: 1rem;
}

@media screen and (min-width: 768px) {
  .underline {
    width: 6%;
  }
  .btn-container button {
    font-size: 14px;
  }
  .menu-item {
    grid-template-columns: 225px 1fr;
    gap: 0 1.25rem;
    max-width: 40rem;
  }
  .photo {
    height: 175px;
  }
}
@media screen and (min-width: 1200px) {
  .main-container {
    width: 95vw;
    grid-template-columns: 1fr 1fr;
  }
  .photo {
    height: 150px;
  }
}

JS

const menu = [
  {
    id: 1,
    title: "등심샐러드",
    category: "APPETIZER",
    price: "22,000원",
    img: "./img/APPETIZER1.jpeg",
    desc: `소고기 등심에 만다린드레싱으로 맛을 낸 샐러드 * 만다린 : 오렌지과에 속하는 감귤`,
  },
  {
    id: 2,
    title: "깔조네 리코타",
    category: "APPETIZER",
    price: "20,000원",
    img: "./img/APPETIZER2.jpeg",
    desc: `신선한 야채와 과일, 수제 리코타치즈를 화덕빵에 싸먹는 샐러드`,
  },
  {
    id: 3,
    title: "수제 유자에이드",
    category: "DRINK",
    price: "7,000원",
    img: "./img/DRINK1.jpeg",
    desc: `상큼한 수제 유자에이드 ※ 블루, 레드, 옐로우, 그린 선택`,
  },
  {
    id: 4,
    title: "어부의 만찬(오일)",
    category: "PASTA",
    price: "25,000원",
    img: "./img/PASTA1.jpeg",
    desc: `싱싱한 해산물로 맛을 낸 피셔맨스키친 시그니처 파스타 ※ 토마토, 크림, 오일 선택`,
  },
  {
    id: 5,
    title: "새우 로제",
    category: "PASTA",
    price: "25,000원",
    img: "./img/PASTA2.jpeg",
    desc: `큼직한 새우와 분홍빛의 토마토크림소스로 맛을 낸 로제파스타`,
  },
  {
    id: 6,
    title: "라구 리가토니(건면)",
    category: "PASTA",
    price: "24,000원",
    img: "./img/PASTA3.jpeg",
    desc: `72시간 푹끓인 라구소스로 만든 나폴리식 토마토파스타 ※ 리가토니 : 숏파스타면의 한종류`,
  },
  {
    id: 7,
    title: "비스마르크",
    category: "PIZZA",
    price: "25,000원",
    img: "./img/PIZZA1.jpeg",
    desc: `리코타치즈, 프로볼로네, 피오르디라떼, 모르타텔라, 트러플오일로 맛을 낸 담백한피자 * 프로볼로네 : 이탈리아 남부 캄파니아 지방의 특산치즈 * 피오르디라떼 : 모짜렐라치즈의 한 종류로 젖산발효균으로 만든 나폴리 화덕피자 전용치즈 * 모르타텔라 : 돼지어깨의 살고기와 목부위의 지방을 섞어 만든 이탈리아생햄`,
  },
  {
    id: 8,
    title: "디아볼로",
    category: "PIZZA",
    price: "24,000원",
    img: "./img/PIZZA2.jpeg",
    desc: `살라미햄과 양파,페퍼로치노를 올려 만든 클래식한 이탈리아에 대표적인 매콤한 맛의 피자`,
  },
  {
    id: 9,
    title: "프로슈토 루꼴라",
    category: "PIZZA",
    price: "26,000원",
    img: "./img/PIZZA3.jpeg",
    desc: `렌치소스에 프로슈토생햄과 루꼴라로 맛을 낸 피셔맨스키친 시그니처피자`,
  },
  {
    id: 10,
    title: "소고기 버섯 리조또",
    category: "RISOTTO",
    price: "24,000원",
    img: "./img/RISOTTO1.jpeg",
    desc: `최상급 소고기와 포르치니 버섯으로 맛을 낸 리조또 ※ 포르치니 : 버섯중에 왕이라 불리는 이탈리아 대표 식재료`,
  },
  {
    id: 11,
    title: "먹물관자 리조또",
    category: "RISOTTO",
    price: "24,000원",
    img: "./img/RISOTTO2.jpeg",
    desc: `팬프라이한 신선한 관자, 먹물크림으로 맛을 낸 리조또`,
  },
  {
    id: 12,
    title: "채끝등심300g",
    category: "STEAK",
    price: "49,000원",
    img: "./img/STEAK1.jpeg",
    desc: `40일 숙성후 웻에징한 그릴 등심스테이크 ※ 스테이크에 제공되는 소금은 일반소금이 아닌 '말돈소금'으로 영국황실에서만 사용했던 칼슘과 칼륨, 마그네슘 함량이 풍부한 건강식 '고급소금' 입니다.`,
  },
  {
    id: 13,
    title: "씨푸드와 채끝등심300g",
    category: "STEAK",
    price: "63,000원",
    img: "./img/STEAK2.jpeg",
    desc: `갈릭 오일로 구워 낸 해산물과 채끝등심 스테이크 ※ 스테이크에 제공되는 소금은 일반소금이 아닌 '말돈소금'으로 영국황실에서만 사용했던 칼슘과 칼륨, 마그네슘 함량이 풍부한 건강식 '고급소금' 입니다.`,
  },
];
// get parent element
const mainContainer = document.querySelector(".main-container");
const btnContainer = document.querySelector(".btn-container");
// display all items when page loads
window.addEventListener("DOMContentLoaded", function () {
  displayMenuItems(menu);
  displayMenuButtons();
});

function displayMenuItems(menuItems) {
  let displayMenu = menuItems.map(function (item) {
    return `<div class="menu-item">
          <img src=${item.img} alt=${item.title} class="photo" />
          <div class="item-info">
            <header>
              <h4>${item.title}</h4>
              <h4 class="price">${item.price}</h4>
            </header>
            <p class="item-text">
              ${item.desc}
            </p>
          </div>
        </div>`;
  });
  displayMenu = displayMenu.join("");

  mainContainer.innerHTML = displayMenu;
}
function displayMenuButtons() {
  const categories = menu.reduce(
    function (values, item) {
      if (!values.includes(item.category)) {
        values.push(item.category);
      }
      return values;
    },
    ["All"]
  );
  const categoryBtns = categories
    .map(function (category) {
      return `<button type="button" class="filter-btn" data-id=${category}>
          ${category}
        </button>`;
    })
    .join("");

  btnContainer.innerHTML = categoryBtns;
  const filterBtns = btnContainer.querySelectorAll(".filter-btn");
  console.log(filterBtns);

  filterBtns.forEach(function (btn) {
    btn.addEventListener("click", function (e) {
      // console.log(e.currentTarget.dataset);
      const category = e.currentTarget.dataset.id;
      const menuCategory = menu.filter(function (menuItem) {
        // console.log(menuItem.category);
        if (menuItem.category === category) {
          return menuItem;
        }
      });
      if (category === "All") {
        displayMenuItems(menu);
      } else {
        displayMenuItems(menuCategory);
      }
    });
  });
}

 

const menu: 메뉴 항목 정보를 담고 있는 배열입니다. 각 항목은 객체로 표현되며 음식 메뉴의 id, 제목, 카테고리, 가격, 이미지 경로, 설명 의 정보를 포함하고 있습니다.

 

const mainContainer와 const btnContainer: HTML에서 .main-container와 .btn-container 요소를 JavaScript로 선택하여 변수에 할당합니다.

 

window.addEventListener("DOMContentLoaded", function () { ... }): 페이지가 로드될 때, 음식 메뉴 항목을 표시하고 필터링할 버튼도 생성하는 이벤트 리스너입니다.

 

displayMenuItems(menuItems): 이 함수는 메뉴 항목을 표시하는 역할을 합니다. menuItems 매개변수로 받은 배열에 있는 각 항목을 HTML 문자열로 변환하여 .main-container에 삽입합니다.

 

displayMenu = displayMenu.join("");:

displayMenu는 배열의 요소들을 문자열로 결합하기 위해 사용되는 문자열 배열입니다. 이 배열에는 각 음식 메뉴 항목에 대한 HTML 문자열이 들어 있습니다.

.join("") 메서드는 배열의 모든 요소를 하나의 문자열로 결합하는 역할을 합니다. ""는 빈 문자열을 사용하므로 배열 요소 사이에 아무런 공백이나 구분 문자 없이 합쳐집니다.

따라서 이 줄의 코드는 displayMenu 배열의 요소들을 모두 이어붙여서 하나의 큰 HTML 문자열로 만듭니다.

mainContainer.innerHTML = displayMenu;:

이 코드는 화면에 표시되는 메뉴 항목을 업데이트하는 역할을 합니다.

mainContainer는 HTML 문서에서 .main-container라는 클래스를 가진 요소를 찾아서 JavaScript 변수에 할당한 것입니다.

mainContainer.innerHTML은 해당 요소의 내부 HTML 콘텐츠를 변경하는 속성입니다.

displayMenu 저장된 HTML 문자열을 mainContainer.innerHTML 대입함으로써, 화면에 표시되는 메뉴 항목이 새로운 HTML 문자열로 대체됩니다. 이것은 메뉴를 업데이트하거나 필터링할 사용됩니다.

 

displayMenuButtons(): 이 함수는 메뉴 필터링 버튼을 생성하고 이벤트 리스너를 연결합니다. menu 배열에서 고유한 카테고리를 추출하여 버튼을 생성하고, 각 버튼에 카테고리 데이터를 설정합니다.

 

filterBtns.forEach(function (btn) { ... }): 각 필터링 버튼에 대한 이벤트 리스너를 연결합니다. 버튼을 클릭하면 해당 카테고리에 해당하는 메뉴 항목을 표시하거나 "All" 버튼을 클릭하면 모든 메뉴 항목을 표시합니다.

 

displayMenuItems 함수와 displayMenuButtons 함수에서는 배열의 데이터를 HTML 문자열로 변환하여 웹 페이지에 동적으로 삽입합니다. 이를 통해 사용자에게 음식 메뉴 항목을 시각적으로 표시하고 필요에 따라 필터링 할 수 있습니다.

 

페이지를 열면 음식 메뉴가 초기에 모두 표시되며, 카테고리 필터링 버튼을 클릭하여 특정 카테고리의 메뉴 항목만 있습니다. 이것은 사용자가 메뉴를 쉽게 검색하고 선택할 있는 편리한 인터페이스를 제공합니다.

(미완성 글입니다.)

 

링크

https://github.com/jinuk0316/FoodMenu

 

GitHub - jinuk0316/FoodMenu

Contribute to jinuk0316/FoodMenu development by creating an account on GitHub.

github.com

728x90
반응형