Javascript 정리 ⑧

영긔·2024년 6월 12일
0

📒 Vanilla JS

목록 보기
8/8
post-thumbnail

✨ 웹개발 기능대회 예제 구현

앞에서 공부했던 것들을 바탕으로 직접 간단한 쇼핑몰 사이트를 구현해보자

먼저, 큰 틀은 다음과 같다.

html

  <nav class="navbar navbar-light bg-black">
    <div class="container-fluid">
        <span class="navbar-brand text-white">영긔네 장난감 가게</span>
    </div>
</nav>
  <div class="container" style="padding-top: 25px;">
    <input id="search" placeholder="검색어입력">
  </div>


  <div class="container">
    <div class="row product-list">
      <!-- <div class="col-md-3">
        <img src="">
        <h4>상품명 : </h4>
        <p>가격 : </p>
      </div> -->
    </div>

  </div>

  <div class="container basket-wrap" style="background : #e2e2e2">
    <h4>장바구니 (드래그 가능)</h4>
    <div class="row basket" ondrop="drop(event)" ondragover="allowDrop(event)">

    </div>
  </div>
  <div class="container my-3">
    <h4>최종가격 : <span class="final-price"></span></h4>

    <button class="buy" onclick="buyItem()">구매하기</button>
  </div>

css

body {
    background: #f4f4f4;
}
button {
    background: black;
    color: white;
    border-radius: 3px;
    border: none;
    padding: 5px 12px 5px 12px;
}
img {
    width: 100%;
    margin-bottom: 20px;
}

.basket {
    min-height: 300px;
    width: 100%;
    background: black;
    padding: 20px;
}

.basket-wrap {
    padding: 30px;
    margin-top: 30px;
}
.basket div {
    padding: 15px;
    border-radius: 5px;
}

.item {
    width: 300px;
    background: white;
    padding: 20px;
    margin-top: 20px;
} 

.modal1, .modal2 {
    position: fixed;
    margin: auto;
    padding: 30px;
    top: 0px;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
}

.black-bg {
    display: none;
    width: 100%;
    height: 100%;
    position: fixed;
    background: rgba(0, 0, 0, 0.5);
    z-index: 5;
    padding: 30px;
    /* visibility: hidden;
    opacity: 0;
    transition: all 1s; */
  }
  .white-bg {
    background: white;
    border-radius: 5px;
    padding: 30px;
  }
  .show-modal {
    display: block;
  }
  .hide{
    display: none;
  }

💡 검색 기능

해당 단어를 검색하면 노란색으로 표시되게 해보자

json

{
  "products": [
    {
      "id": 0,
      "title": "곰돌이",
      "brand": "(주)곰돌과 영긔",
      "photo": "s1.jpg",
      "price": 10000
    },
    {
      "id": 1,
      "title": "토끼",
      "brand": "(주)토끼와 영긔",
      "photo": "s2.jpg",
      "price": 20000
    },
    {
      "id": 2,
      "title": "우비 세트",
      "brand": "(주)우비와 영긔",
      "photo": "s3.jpg",
      "price": 30000
    },
    {
      "id": 3,
      "title": "펭귄 세트",
      "brand": "(주)펭귄과 영긔",
      "photo": "s4.jpg",
      "price": 40000
    }
  ]
}

js

/**********
 * 상품진열 *
 ***********/
//같은 폴더 안에 store.json을 두었다.
fetch("store.json")
  .then((response) => response.json())
  .then((data) => {
    //원본데이터 다른데서 많이 쓰니까 변수에 보관
    products = data.products;

    //페이지로드시 json 데이터가져와서 메인페이지 내용 만들기
    show(data.products);
  })
  .catch((error) => console.error("Fetch error:", error));
/********************
 * 상품 보여주는 함수 *
 ********************/
function show(data) {//상품을 나열합니다.
  data.forEach((a, i) => {//데이터에 대해 반복문을 돕니다.
    document.querySelector(".product-list").insertAdjacentHTML(//html 삽입
      "beforeend",//앞에 나열한다는 뜻
      `
          <div class="col-md-3">
            <div class="item" draggable="true" id="item-${a.id}">
              <img src="${a.photo}" draggable="false">
              <h4 class="title">${a.title}</h4>
              <h4 class="brand">${a.brand}</h4>
              <p class="price">가격 : ${a.price}</p>
              <button class="add" data-id="${a.id}">담기</button>
            </div>
          </div>`//html을 작성합니다.
    );//드래그할 수 있도록 draggable을 true로 주고 ongragstart를 주었습니다.
  });
}
/**********
 * 상품검색 *
 ***********/
document.getElementById("search").addEventListener("input", function () {
  let sText = this.value;//입력된 검색어를 input할때마다 가져와 sText로 할당합니다.
  //이전 거 초기화합니다.
  document.querySelector(".product-list").innerHTML = "";
  let searchData = products.filter((a) => {//filter를 사용하여 걸러줍니다.
    //includes를 사용하여 단어가 있는지 검사합니다.
    return a.title.includes(sText) || a.brand.includes(sText);
  });
  //앞에 작성한 상품나열 함수에 데이터를 넣습니다.
  show(searchData);
  // .product-list 클래스를 가진 요소를 선택합니다.
  var productElements = document.querySelectorAll(".product-list h4");

  // 각 요소에 대해 반복합니다.
  productElements.forEach(function (a) {
    var title = a.innerHTML;

    // 검색어를 찾아서 강조합니다.
    title = title.replace(
      sText,
      `<span style="background : yellow">${sText}</span>`
    );

    // 요소의 innerHTML을 변경하여 강조된 검색어가 포함된 제목을 표시합니다.
    a.innerHTML = title;
  });
});

💡 장바구니 담기

📍 버튼으로 담기

/***********************
 * 장바구니 버튼으로 담기 *
 ***********************/
//최종가격 초기화
var finalPrice = 0;
//버튼으로 담는 함수. 앞에서 '담기'버튼을 누르면 handleDrop()이 작동하게 작성
function handleDrop(ev) {
  ev.preventDefault();
  //담기 버튼이 ev이므로 부모 요소를 가져온다.
  var target = ev.target.parentElement;
  var data = target.id;//식별 아이디
  var imgData = target.querySelector("img").src;//이미지
  var titleData = target.querySelector(".title").innerText;//상품명
  var brandData = target.querySelector(".brand").innerText;//브랜드명
  var priceData = target.querySelector(".price").innerText;//가격
  //장바구니에서 식별 아이디로 이미 담겨있는 상품들을 가져온다.
  const existingItem = document.querySelector(".basket")
  .querySelector(`#${data}`);
  if (existingItem) {//해당 상품이 장바구니에 이미 있을 경우
    //해당 상품의 갯수
    let countElement = existingItem.querySelector(".count");
    //해당 상품의 갯수를 정수화시켜준다. 더해주기 위해
    let number = parseInt(countElement.value);
    number += 1;
    countElement.value = number;//1더해주고 다시 값을 넣어준다.
    //앞에 붙어있는 '가격 : '을 떼준값을 최종가격에 더해준다.
    finalPrice += parseInt(priceData.substr(5));
  } else {//장바구니에 해당 상품이 없을 경우
    let number = 1;
    document.querySelector(".basket").insertAdjacentHTML(
      "beforeend",
      `
                <div class="col-md-3 white-bg" id="${data}">
                  <div>
                    <img src="${imgData}" alt="Product Image">
                    <h4 class="title">${titleData}</h4>
                    <h4 class="brand">${brandData}</h4>
                    <p class="price">${priceData}</p>
                    <p>수량: <input type="number" class="count" value="${number}" min="1"></p>
                    
                  </div>
                </div>`
    );//장바구니에 해당 상품 넣어준다.
    finalPrice += parseInt(priceData.substr(5));//최종가격 업데이트
  }
  //해당 아이템의 갯수 가져옴
  const newItem = document.querySelector(`#${data} .count`);
  //blur(포커스를 잃는 순간)일때 update 함수 실행
  newItem.addEventListener("blur", update);
  //업데이트한 최종가격 화면에 반영
  document.querySelector(".final-price").innerText = finalPrice;
}
//최종가격 업데이트 실행
function update() {
  finalPrice = 0;//초기화
  //장바구니에 있는 아이템 반복돌림
  document.querySelectorAll(".basket .col-md-3").forEach((item) => {
    const price = parseInt(
      item.querySelector(".price").innerText.replace("가격 : ", "")
    );
    const count = parseInt(item.querySelector(".count").value);
    //값, 수량 구해와서 곱한 거 더해줘서 최종가격 구함
    finalPrice += price * count;
  });
  document.querySelector(".final-price").innerText = finalPrice;
}

📍 드래그로 담기


우비세트를 두번 드래그 하자 수량이 늘어나는 것을 볼 수 있다.

장바구니에 담으면 최종가격이 업데이트 된다.

/****************
 * 드래그 앤 드롭 *
 ****************/
//최종가격 초기화
var finalPrice = 0;
//장바구니에 드롭놓을때 실행
function allowDrop(ev) {
  ev.preventDefault();
}
//해당 상품 드래그할 때 실행
function drag(ev) {
  //dataTransfer.setData로 드래그할 데이터를 전달한다.
  ev.dataTransfer.setData("text", ev.target.id);//id
  ev.dataTransfer.setData("img", ev.target.querySelector("img").src);//이미지
  ev.dataTransfer.setData("title", ev.target.querySelector(".title").innerText);//상품명
  ev.dataTransfer.setData("brand", ev.target.querySelector(".brand").innerText);//브랜드명
  ev.dataTransfer.setData("price", ev.target.querySelector(".price").innerText);//가격
}
//장바구니에 드롭할 때 실행
function drop(ev) {
  ev.preventDefault();
  //dataTransfer.getData로 데이터를 가져온다.
  var data = ev.dataTransfer.getData("text");
  var imgData = ev.dataTransfer.getData("img");
  var titleData = ev.dataTransfer.getData("title");
  var brandData = ev.dataTransfer.getData("brand");
  var priceData = ev.dataTransfer.getData("price");
  //장바구니에 드롭할 때
  if (ev.target.classList.contains("basket")) {
    //id로 드래그한 상품이 있는지 찾음
    const existingItem = ev.target.querySelector(`#${data}`);
    //만약 있다면
    if (existingItem) {
      let countElement = existingItem.querySelector(".count");
      let number = parseInt(countElement.value);
      //상품 갯수 불러와 1더해주고 다시 값 넣어줌
      number += 1;
      countElement.value = number;
      //최종가격 업데이트
      finalPrice += parseInt(priceData.substr(5));
    } else {//아닐경우
      let number = 1;
      ev.target.insertAdjacentHTML(
        "beforeend",
        `
                <div class="col-md-3 white-bg" id="${data}">
                  <div>
                    <img src="${imgData}" alt="Product Image">
                    <h4 class="title">${titleData}</h4>
                    <h4 class="brand">${brandData}</h4>
                    <p class="price">${priceData}</p>
                    <p>수량: <input type="number" class="count" value="${number}" min="1"></p>
                    
                  </div>
                </div>`
      );
      finalPrice += parseInt(priceData.substr(5));
    }
  }
  document.querySelector(".final-price").innerText = finalPrice;
}
//최종가격 업데이트 실행
function update() {
  finalPrice = 0;//초기화
  //장바구니에 있는 아이템 반복돌림
  document.querySelectorAll(".basket .col-md-3").forEach((item) => {
    const price = parseInt(
      item.querySelector(".price").innerText.replace("가격 : ", "")
    );
    const count = parseInt(item.querySelector(".count").value);
    //값, 수량 구해와서 곱한 거 더해줘서 최종가격 구함
    finalPrice += price * count;
  });
  document.querySelector(".final-price").innerText = finalPrice;
}

💡 수량 입력하기

직접 수량을 입력하면 수량이 변경되고, 최종가격이 업데이트된다.

//최종가격 업데이트 실행
function update() {
  finalPrice = 0;//초기화
  //장바구니에 있는 아이템 반복돌림
  document.querySelectorAll(".basket .col-md-3").forEach((item) => {
    const price = parseInt(
      item.querySelector(".price").innerText.replace("가격 : ", "")
    );
    const count = parseInt(item.querySelector(".count").value);
    //값, 수량 구해와서 곱한 거 더해줘서 최종가격 구함
    finalPrice += price * count;
  });
  //이 부분이 수량 직접 입력하면 업데이트해주는 코드
  document.querySelector(".final-price").innerText = finalPrice;
}

💡 모달창으로 사용자 정보 입력받기


구매하기를 누르면 모달창이 표시되도록 한다.

html

  <div class="modal1" style="display: none;">
    <div class="white-bg">
      <h4>성함</h4>
      <input type="text" id="name">
      <h4>연락처</h4>
      <input type="text" id="phone">
      <button class="show-receipt">입력완료</button>
      <div>
        <button class="close">닫기</button>
      </div>
    </div>
  </div>

js

/**********
 * 상품구매 *
 ***********/
//입력폼
function buyItem() {
  document.querySelector(".modal1").style.display = "block";
}
document.querySelector(".modal1 .close").addEventListener("click", function () {
  document.querySelector(".modal1").style.display = "none";
});

💡 영수증 이미지 출력


입력완료 버튼을 누를 시 영수증 이미지를 보여준다. 이때 영수증 이미지에는 구매한 상품들과 최종가격을 나열해준다.

html

  <div class="modal2" style="display: none;">
    <div class="white-bg">
      <h4>영수증</h4>
      <canvas id="canvas" width="350" height="350"></canvas>
      <div>
        <button class="close">닫기</button>
      </div>
    </div>
  </div>

js

//구매 영수증 이미지 표시
document.querySelector(".show-receipt").addEventListener("click", function () {
  //이전 모달창 닫기
  document.querySelector(".modal1").style.display = "none";
  //영수증 모달창 보이기
  document.querySelector(".modal2").style.display = "block";
  //캔버스 만들어줌
  var canvas = document.getElementById("canvas");
  var c = canvas.getContext("2d");
  c.font = "20px dotum";
  //최종가격 초기화
  finalPrice = 0;
  //칸 띄워줄때 쓸 변수
  var i = 1;
  //장바구니에 있는 상품 반복문 돌리기
  document.querySelectorAll(".basket .col-md-3").forEach((item) => {
    const title = item.querySelector(".title").innerText;
    const price = parseInt(
      item.querySelector(".price").innerText.replace("가격 : ", "")
    );
    const count = parseInt(item.querySelector(".count").value);
    //최종가격 구해줌
    finalPrice += price * count;
    c.fillText(title, 30, i * 70 - 20);//상품명 표시.두 세번째 파라미터는 위치
    c.fillText("가격 : " + price, 30, i * 70 + 5);//가격 표시
    c.fillText("수량 : " + count, 30, i * 70 + 30);//수량 표시
    i++;
  });
  document.querySelector(".final-price").innerText = finalPrice;
  //영수증 마지막에 최종가격 표시
  c.fillText("최종 가격 : " + finalPrice, 30, i * 70 - 20);
});

//영수증 닫기
document.querySelector(".modal2 .close").addEventListener("click", function () {
  document.querySelector(".modal2").style.display = "none";
  var canvas = document.getElementById("canvas");
  var c = canvas.getContext("2d");
  //영수증 초기화. 초기화해주지 않으면 닫고 다시 상품추가할때 엉망으로 나옴
  c.clearRect(0, 0, canvas.width, canvas.height);
});

✨ 결과


0.5x로 설정하는 것을 추천한다.(모바일은 0.25x)

profile
SKYDeveloper

2개의 댓글

comment-user-thumbnail
2024년 6월 14일

짱이에요

1개의 답글