image

select custom

태그
HtmlCssJavascript
상세설명select custom 제작
작성일자2024.01.13

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>

    결과