Javascript

select custom

minsun309 2024. 8. 26. 09:31

Html에 있는 select태그, option태그로는 커스텀에 한계가 있어 직접 만들어보는 방법을 알아보았다.

Html

기본 구조는 select태그 역할을 하는 선택된 값과 option들 값들을 분리하여 클래스 명이 selectBox인 div 태그 안에 배치 한다.

<section>
   <!-- 1번째 select -->
   <div class="selectBox">
     <div class="selected">
       <div class="selected-value">drink</div>
       <div class="arrow">
          <img src="/public/image/downArrow.svg" alt="arrow" />
       </div>
     </div>
     <ul class="optionList">
        <li class="option">coffee</li>
        <li class="option">water</li>
        <li class="option">latte</li>
        <li class="option">jucie</li>
      </ul>
   </div>

   <!-- 2번째 select -->
   <div class="selectBox">
      <div class="selected">
        <div class="selected-value">food</div>
        <div class="arrow">
          <img src="/public/image/downArrow.svg" alt="arrow" />
        </div>
      </div>
      <ul class="optionList">
         <li class="option">pizza</li>
         <li class="option">rice</li>
         <li class="option">burger</li>
         <li class="option">bread</li>
       </ul>
    </div>
</section>

 

 

Css

원하는 모양으로 커스텀을 한다.

  • 아래 코드는 option들이 클릭 시 나타날 때 스르륵 나타났으면 해서 optionList (= ul) 기본으로 max-height: 0; overflow: hidden; 값을 주고 selectBox에 active 클래스 명이 추가되면 optionList (= ul) 에 max-height: 500px; 값을 주었다.
section {
   display: flex;
   align-items: center;
   justify-content: center;
   font-size: 14px;
   margin-top: 20px;
}

.selectBox {
   cursor: pointer;
   margin-right: 20px;
}

.selected {
   width: 150px;
   display: flex;
   align-items: center;
   justify-content: space-between;
   padding: 8px 5px;
   border-radius: 4px;
   border: 2px solid rgb(0, 180, 45);
 }

 .select .selected .selected-value {
   max-width: 90px;
 }

 .arrow {
   display: flex;
   align-items: center;
   justify-content: center;
   width: 10px;
   height: 10px;
 }

 .arrow img {
   width: 100%;
 }

 .selectBox .optionList {
   position: absolute;
   margin-top: 4px;
   width: 150px;
   border-radius: 4px;
   background: #fff;
   cursor: pointer;
   max-height: 0;
   overflow: hidden;
   transition: 0.5s ease-in;
 }

 .selectBox .option {
   border-left: 2px solid rgb(0, 180, 45);
   border-right: 2px solid rgb(0, 180, 45);
 }

 .selectBox .option:first-of-type {
   border-top: 2px solid rgb(0, 180, 45);
 }

.selectBox .option:last-of-type {
  border-bottom: 2px solid rgb(0, 180, 45);
}

.selectBox.active .optionList {
   max-height: 500px;
}

.selectBox.active .arrow {
   transform: scaleY(-1);
}

.option {
   padding: 8px 5px;
}

.option:hover {
   background: rgb(180, 211, 188);
}

 

 

Javascript

Javascript을 통해 select 기능을 만들었다.

  • active 클래스 명을 추가하거나 삭제하는 토글 기능을 toggleSelectBox() 함수를 통해 구현하고 selectOption() 함수를 통해 선택된 한 option 값으로 변경되게 했다.
  • selectBox 가 여러개일 경우를 대비해 forEach()문을 통해 각 selectBox 에 클릭 이벤트를 추가하여 toggleSelectBox(), selectOption() 함수가 실행되게 했다.
  • 마지막으로 문서 전체에 클릭 이벤트 리스너 추가하여 selectBox 이외의 부분을 클릭했을 때, 옵션을 클릭했을 때 모든 selectBox에서 'active' 클래스 제거했다.
<script>
      // 모든 셀렉션 박스 엘리먼트 선택
      const selectBoxElements = document.querySelectorAll(".selectBox");

      //.optionList
      function toggleSelectBox(selectBox) {
        selectBox.classList.toggle("active");
      }

      // 옵션 선택 함수 / closest() - 가장 가까운 조상
      function selectOption(optionElement) {
        const selectBox = optionElement.closest(".selectBox");
        const selectedElement = selectBox.querySelector(".selected-value");
        selectedElement.textContent = optionElement.textContent;
      }

      // 각 셀렉션 박스에 클릭 이벤트 리스너 추가
      selectBoxElements.forEach((selectBoxElement) => {
        selectBoxElement.addEventListener("click", function (e) {
          const targetElement = e.target;
          const isOptionElement = targetElement.classList.contains("option");

          if (isOptionElement) {
            selectOption(targetElement);
          }

          toggleSelectBox(selectBoxElement);
        });
      });

      // 문서 전체에 클릭 이벤트 리스너 추가
      // 모든 셀렉션 박스 이외의 부분을 클릭했을 때,
      // 모든 셀렉션 박스에서 'active' 클래스를 제거하여 옵션 목록을 숨긴다.
      document.addEventListener("click", function (e) {
        const targetElement = e.target;
        const isSelect =
          targetElement.classList.contains("selectBox") ||
          targetElement.closest(".selectBox");

        if (isSelect) {
          return;
        }

        // 모든 셀렉션 박스에서 'active' 클래스 제거
        const allSelectBoxElements = document.querySelectorAll(".selectBox");

        allSelectBoxElements.forEach((boxElement) => {
          boxElement.classList.remove("active");
        });
      });
 </script>

 

 

전체코드

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <style>
      section {
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 14px;
        margin-top: 20px;
      }

      .selectBox {
        cursor: pointer;
        margin-right: 20px;
      }

      .selected {
        width: 150px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 8px 5px;
        border-radius: 4px;
        border: 2px solid rgb(0, 180, 45);
      }

      .select .selected .selected-value {
        max-width: 90px;
      }

      .arrow {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 10px;
        height: 10px;
      }

      .arrow img {
        width: 100%;
      }

      .selectBox .optionList {
        position: absolute;
        margin-top: 4px;
        width: 150px;
        border-radius: 4px;
        background: #fff;
        cursor: pointer;
        max-height: 0;
        overflow: hidden;
        transition: 0.5s ease-in;
      }

      .selectBox .option {
        border-left: 2px solid rgb(0, 180, 45);
        border-right: 2px solid rgb(0, 180, 45);
      }

      .selectBox .option:first-of-type {
        border-top: 2px solid rgb(0, 180, 45);
      }

      .selectBox .option:last-of-type {
        border-bottom: 2px solid rgb(0, 180, 45);
      }

      .selectBox.active .optionList {
        max-height: 500px;
      }

      .selectBox.active .arrow {
        transform: scaleY(-1);
      }

      .option {
        padding: 8px 5px;
      }

      .option:hover {
        background: rgb(180, 211, 188);
      }
    </style>
  </head>
  <body>
    <section>
      <div class="selectBox">
        <div class="selected">
          <div class="selected-value">drink</div>
          <div class="arrow">
            <img src="/public/image/downArrow.svg" alt="arrow" />
          </div>
        </div>
        <ul class="optionList">
          <li class="option">coffee</li>
          <li class="option">water</li>
          <li class="option">latte</li>
          <li class="option">jucie</li>
        </ul>
      </div>
      <div class="selectBox">
        <div class="selected">
          <div class="selected-value">food</div>
          <div class="arrow">
            <img src="/public/image/downArrow.svg" alt="arrow" />
          </div>
        </div>
        <ul class="optionList">
          <li class="option">pizza</li>
          <li class="option">rice</li>
          <li class="option">burger</li>
          <li class="option">bread</li>
        </ul>
      </div>
    </section>
    <script>
      // 모든 셀렉션 박스 엘리먼트 선택
      const selectBoxElements = document.querySelectorAll(".selectBox");

      //.optionList
      function toggleSelectBox(selectBox) {
        selectBox.classList.toggle("active");
      }

      // 옵션 선택 함수 / closest() - 가장 가까운 조상
      function selectOption(optionElement) {
        const selectBox = optionElement.closest(".selectBox");
        const selectedElement = selectBox.querySelector(".selected-value");
        selectedElement.textContent = optionElement.textContent;
      }

      // 각 셀렉션 박스에 클릭 이벤트 리스너 추가
      selectBoxElements.forEach((selectBoxElement) => {
        selectBoxElement.addEventListener("click", function (e) {
          const targetElement = e.target;
          const isOptionElement = targetElement.classList.contains("option");

          if (isOptionElement) {
            selectOption(targetElement);
          }

          toggleSelectBox(selectBoxElement);
        });
      });

      // 문서 전체에 클릭 이벤트 리스너 추가
      // 모든 셀렉션 박스 이외의 부분을 클릭했을 때,
      // 모든 셀렉션 박스에서 'active' 클래스를 제거하여 옵션 목록을 숨긴다.
      document.addEventListener("click", function (e) {
        const targetElement = e.target;
        const isSelect =
          targetElement.classList.contains("selectBox") ||
          targetElement.closest(".selectBox");

        if (isSelect) {
          return;
        }

        // 모든 셀렉션 박스에서 'active' 클래스 제거
        const allSelectBoxElements = document.querySelectorAll(".selectBox");

        allSelectBoxElements.forEach((boxElement) => {
          boxElement.classList.remove("active");
        });
      });
    </script>
  </body>
</html>

 

 

결과