본문 바로가기

Language & Framework/실습

바닐라 자바스크립트 쇼핑몰 정렬 기능 구현하기 (map, filter, sort)

버튼을 누를 때 셀렉트의 값도 바뀌게 하고 싶으면 버튼 이벤트에 Select.value 값을 바꾸게 지정하세요.

 

서버에서 값 받아와서 어쩌고 저쩌고 그런 글 아닙니다.

그냥 깡 자바스크립트로 정렬 기능 구현만 한 것.

실습 카테고리에 있는 이유는 정보 전달의 목적으로 작성하기에는 너무 단순무식한 방법으로 만들었기 때문이니 저처럼 완전 초보가 아닌 이상 읽고 참고할만한 게 없을 것이라고 판단했기 때문입니다.

 

 

HTML

    <section class="shop">
        <h2 class="shop__title">멍멍이 용품 팝니다</h2>
        <select name="shop__selector" id="shop__selector">
            <option>기본 정렬</option>
            <option>가나다순</option>
            <option>낮은 가격순</option>
            <option>높은 가격순</option>
            <option>5만원 이하</option>
            <option>달러</option>
        </select>
        <div class="shop__buttonContainer">
            <button class="shop__basicBtn">기본 정렬</button>
            <button class="shop__AbcBtn">가나다순</button>
            <button class="shop__lowPriceBtn">낮은 가격순</button>
            <button class="shop__highPriceBtn">높은 가격순</button>
            <button class="shop__priceLimitBtn">5만원 이하</button>
            <button class="shop__exchangeBtn">달러</button>
        </div>
        <div class="shop___card__container">
        </html>

상품 개수가 적어졌다 많아졌다 반복하는 것을 클래스를 붙이고 떼는 것으로 구현하면 좋겠다고 생각했는데, 마땅히 방법이 생각나지 않았고 구글링을 해봐도 보고 배울만한 글을 찾지 못해서 그냥 container만 만들고 비워놓은 상태에서 자바스크립트로 메꿨습니다.

 

 

CSS

.shop {
  padding: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;

  button {
    width: 100px;
    padding: 5px;
    margin: 5px;
    border: none;
    background-color: lightyellow;
    cursor: pointer;
  }
}

.shop___card__container {
  display: flex;
}

.shop__card {
  background-color: beige;
  padding: 30px;
  margin: 20px;
  width: 300px;
  height: 200px;
  box-shadow: 1px 1px 4px 1px rgb(194, 194, 159);
  text-align:left img {
    width: 300px;
    height: 300px;
  }

  .order {
    border: none;
    background-color: lightcoral;
    padding: 5px;
    cursor: pointer;
  }
}

별 거 없습니다.

 

 

 

Java Scripit 

// 상품 정보
let products = [
  { id: 0, price: 5000, title: "댕댕이 개껌" },
  { id: 1, price: 80000, title: "멍멍이 방석" },
  { id: 2, price: 30000, title: "강아지 사료" },
];
// 셀렉터
const ShopSelector = document.querySelector("#shop__selector");
// 컨테이너
const CardsContainer = document.querySelector(".shop___card__container");
// 버튼
const BtnContainer = document.querySelector(".shop__buttonContainer");
const LowPriceBtn = document.querySelector(".shop__lowPriceBtn");
const HighPriceBtn = document.querySelector(".shop__highPriceBtn");
const PriceLimitBtn = document.querySelector(".shop__priceLimitBtn");
const ExchangeBtn = document.querySelector(".shop__exchangeBtn");
const ABCBtn = document.querySelector(".shop__AbcBtn");
const BasicBtn = document.querySelector(".shop__basicBtn");
// HTML
const ShopCard = `<div class="shop__card" id="0">
<h3 class="title"></h3>
<p><span class="price"></span></p>
<button class="order">주문하기
</button>
</div>`;

기본 세팅입니다. 별다른 건 없습니다.

 

 

// 초기 배열
for (i = 0; i < 3; i++) {
  CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
  const CardsTitle = document.querySelectorAll(".title");
  const CardsPrice = document.querySelectorAll(".price");
  CardsPrice[i].insertAdjacentHTML("beforeend", `${products[i].price}`);
  CardsTitle[i].insertAdjacentHTML("beforeend", `${products[i].title}`);
}

처음 페이지에 접속하거나 새로고침하면 나오는 기본 배열 세팅입니다.

 

// 오름차순
function ProductSortUp() {
  while (CardsContainer.hasChildNodes()) {
    CardsContainer.removeChild(CardsContainer.firstChild);
  }
  const NewProducts = [...products];
  NewProducts.sort(function (a, b) {
    return a.price - b.price;
  });
  for (i = 0; i < NewProducts.length; i++) {
    CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
    const CardsTitle = document.querySelectorAll(".title");
    const CardsPrice = document.querySelectorAll(".price");
    CardsPrice[i].insertAdjacentHTML("beforeend", `${NewProducts[i].price}`);
    CardsTitle[i].insertAdjacentHTML("beforeend", `${NewProducts[i].title}`);
  }
}

// 내림차순
function ProductSortDown() {
  while (CardsContainer.hasChildNodes()) {
    CardsContainer.removeChild(CardsContainer.firstChild);
  }
  const NewProducts = [...products];
  NewProducts.sort(function (a, b) {
    return b.price - a.price;
  });
  for (i = 0; i < NewProducts.length; i++) {
    CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
    const CardsTitle = document.querySelectorAll(".title");
    const CardsPrice = document.querySelectorAll(".price");
    CardsPrice[i].insertAdjacentHTML("beforeend", `${NewProducts[i].price}`);
    CardsTitle[i].insertAdjacentHTML("beforeend", `${NewProducts[i].title}`);
  }
}

 

오름차순 및 내림차순 정렬 함수입니다.

카드 순서를 바꾸는 것을 매번 지웠다가 새로 삽입하는 방식으로 구현했습니다.

컨테이너에 있는 모든 자식 요소를 삭제한 뒤 sort 메서드를 이용하여 각각 가격별로 내림차순, 오름차순으로 정렬하고 for문에는 나름대로의 확장성을 고려하여 배열의 길이를 기준으로 반복문을 작성했습니다.

 

-> products에 바로 .sort를 사용하지 않고 NewProducts에 배열을 복제해서 정렬한 이유?

sort 함수를 사용하면 배열 자체를 변형시키기 때문에 기본 정렬로 되돌릴 방법이 없습니다.

기본 정렬 버튼을 누르면 페이지에서 처음 보이던 순서대로 상품이 노출되기를 원했기 때문에 배열을 복제했습니다.

 

 

//가나다순
function ProductABC() {
  while (CardsContainer.hasChildNodes()) {
    CardsContainer.removeChild(CardsContainer.firstChild);
  }
  const NewProducts = [...products];
  NewProducts.sort(function (a, b) {
    if (a.title < b.title) return -1;
    else if (a.title == b.title) return 0;
    else return 1;
  });
  for (i = 0; i < products.length; i++) {
    CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
    const CardsTitle = document.querySelectorAll(".title");
    const CardsPrice = document.querySelectorAll(".price");
    CardsPrice[i].insertAdjacentHTML("beforeend", `${NewProducts[i].price}`);
    CardsTitle[i].insertAdjacentHTML("beforeend", `${NewProducts[i].title}`);
  }
}

가나다순 정렬. 별다른 건 없습니다.

ㄱ보다 ㄴ이 크고 ㄴ보다 ㄷ이 크다는 점을 이용하여 낱말을 비교해서 크면 -1을 줘서 뒤로 넘기고 작으면 앞으로 당기는 방식으로 정렬해주면 됩니다.

 

// 필터
function ProductFilter() {
  while (CardsContainer.hasChildNodes()) {
    CardsContainer.removeChild(CardsContainer.firstChild);
  }
  const Newproducts = products.filter(function (a) {
    return a.price <= 50000;
  });
  console.log(Newproducts);
  for (i = 0; i < Newproducts.length; i++) {
    CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
    const CardsTitle = document.querySelectorAll(".title");
    const CardsPrice = document.querySelectorAll(".price");
    CardsPrice[i].insertAdjacentHTML("beforeend", `${Newproducts[i].price}`);
    CardsTitle[i].insertAdjacentHTML("beforeend", `${Newproducts[i].title}`);
  }
}

 

5만원 이하의 제품만 보여주는 필터.

특이사항은 없으나 여기서 Newproducts를 만들어준 것은 배열 변형의 문제가 아니라 원래 문법이 그렇게 사용하도록 되어있기 때문입니다. 별다른 이해를 필요로하는 부분이 아니라 그냥 문법 만든 사람이 시키는대로 작성하면 되는 부분입니다.

 

input text 박스를 이용할 경우 'a.price <= input text value의 값' 이런 식으로 지정해주면 버튼이 아니라 사용자가 원하는대로 가격 설정도 가능하겠죠? 저는 이미 지쳐서 따로 만들어보진 않았습니다.

 

// 환전
function ProductExchange() {
  while (CardsContainer.hasChildNodes()) {
    CardsContainer.removeChild(CardsContainer.firstChild);
  }
  const Newproducts = products.map(function (a) {
    return (a.price * 84) / 100000;
  });
  for (i = 0; i < products.length; i++) {
    CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
    const CardsTitle = document.querySelectorAll(".title");
    const CardsPrice = document.querySelectorAll(".price");
    CardsPrice[i].insertAdjacentHTML("beforeend", `${Newproducts[i]}달러`);
    CardsTitle[i].insertAdjacentHTML("beforeend", `${products[i].title}`);
  }
}

마지막 달러 환산 함수입니다.

사실 원화를 달러로 변환하려고 할 경우 계산기에 두드릴 때는 그냥 원화 * 0.00084 이렇게 입력하시면 됩니다.

근데 자바스크립트한테 그렇게 입력하면 이상한 숫자 나옵니다. 궁금하면 해보세요.

아무튼 컴퓨터는 계산을 위한 세팅을 만들어주지 않으면(계산기처럼) 소수점 계산만큼은 멍청한 친구기 때문에 일단 84로 곱하고 100000으로 나누면 됩니다.

소수점으로 사칙연산을 못할 뿐 결과값이 소수점인 건 괜찮습니다.

 

그리고 여기서 Newproducts는 원본 배열을 통채로 받아서 price만 바꾼 것이 아니라 a.price를 달러로 환산한 배열만 받은 것이기 때문에 CardsPrice에만 Newproducts[i]를 넣어주고 나머지는 products[i]로 넣어주면 됩니다.

 

// 초기 배열로 되돌리기
function ProductBasic() {
  while (CardsContainer.hasChildNodes()) {
    CardsContainer.removeChild(CardsContainer.firstChild);
  }
  for (i = 0; i < 3; i++) {
    CardsContainer.insertAdjacentHTML("beforeend", ShopCard);
    const CardsTitle = document.querySelectorAll(".title");
    const CardsPrice = document.querySelectorAll(".price");
    CardsPrice[i].insertAdjacentHTML("beforeend", `${products[i].price}`);
    CardsTitle[i].insertAdjacentHTML("beforeend", `${products[i].title}`);
  }
}

깜빡하고 지나갔는데 초기 배열로 되돌리기 위한 함수도 만들어놨습니다.

별다른 거 없고 그냥 모든 상품 리스트 지우고 초기 상태 그대로 다시 불러오면 됩니다.

 

 

// 버튼의 경우
BtnContainer.addEventListener("click", (e) => {
  if (e.target == LowPriceBtn) {
    ProductSortUp();
  } else if (e.target == HighPriceBtn) {
    ProductSortDown();
  } else if (e.target == PriceLimitBtn) {
    ProductFilter();
  } else if (e.target == ABCBtn) {
    ProductABC();
  } else if (e.target == BasicBtn) {
    ProductBasic();
  } else if (e.target == ExchangeBtn) {
    ProductExchange();
  } else {
    return;
  }
});

// 셀렉트의 경우
ShopSelector.addEventListener("change", (e) => {
  if (e.target.value == "낮은 가격순") {
    ProductSortUp();
  } else if (e.target.value == "높은 가격순") {
    ProductSortDown();
  } else if (e.target.value == "5만원 이하") {
    ProductFilter();
  } else if (e.target.value == "가나다순") {
    ProductABC();
  } else if (e.target.value == "기본정렬") {
    ProductBasic();
  } else {
    ProductExchange();
  }
});

부끄럽지만 그냥 이렇게 다 갖다 박았습니다. 끝.

좀 더 깔끔하고 예쁘고 깜찍한 코드를 만들고 싶지만 제가 지금까지 배운 지식으로는 이 정도가 한계입니다.

자식 요소 모두 지우는 등의 반복되는 구문은 모두 함수로 만들어서 사용했으면 좀 더 깔끔했을 텐데 다 만들고 글까지 작성하고 나서야 보이네요.

남들한테 보여줄만한 코드를 작성할 수 있도록 더욱 정진하겠습니다.