반응형

 

HTML5 select 태그의 styling 한계

 

HTML5 select 태그는 form에서 자주 쓰이는 input user interface로 여러 옵션을 주고, 그중 선택할 수 있도록 하는 UI입니다. select의 선택지는 option태그로 만들어주는데 이 option 태그가 CSS로 스타일링하는 데에 많은 한계를 가진다는 아쉬움이 있습니다.

 

다음은 select와 option태그로 간단한 스타일링을 한 예시입니다.

 

 

See the Pen Untitled by cocoder16 (@cocoder16) on CodePen.

 

 

option 각 블록은 height이나 padding 값이 적용되지 않고 mouseover상태에 대해서도 css로 스타일링을 할 수 없습니다.

 

 

div 태그를 이용하여 select를 구현하면 자유로운 styling이 가능하다.

 

디자인에 대해 높은 자유도를 가지는 div태그를 이용해서 select를 구현하면 styling의 한계를 벗어날 수 있습니다. 단, 이 경우 javascript를 이용해 셀렉트의 기능을 구현하여 모듈화를 시키는 작업을 해야 합니다. 일단 가장 간단한 형태의 custom select를 구현해보겠습니다.

 

먼저 마크업을 작성합니다.

 

<div class="select">
  <div class="selected">
    <div class="selected-value">none</div>
    <div class="arrow"></div>
  </div>
  <ul>
    <li class="option">none</li>
    <li class="option">option 1</li>
    <li class="option">option 2</li>
    <li class="option">option 3</li>
    <li class="option">loooooooooooooooooong text option</li>
  </ul>
</div>

 

option은 unordered list 의미를 적용하여 ul, li 태그를 사용해봤습니다. 마지막 옵션 값은 일부로 길이가 긴 텍스트를 사용했는데 텍스트가 긴 경우의 퍼블리싱 이슈를 확인하기 위해서입니다.

classname이 selected-value인 div태그는 선택된 값을 보여줄 것입니다.

classname이 arrow인 div태그는 화살표 아이콘을 그려줄 것입니다.

 

이제 이 select가 펼쳐져 있을 때의 모습을 디자인해봅니다.

 

ul 태그를 사용했으니 기본 스타일인 머릿 기호와 들여 쓰기를 없애줍니다.

 

ul {
  list-style-type: none;
  padding-left: 0px;
}

 

그리고 div 요소들을 하나하나 차근차근 스타일링해 나갑니다.

 

.select {
  display: inline-block;
  width: 120px;
  border: 1px solid #999;
}

.select .selected {
  display: flex;
  justify-content: space-between;
  padding: 8px 5px;
}

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

.select .selected .arrow {
  width: 24px;
  background: url("https://uxwing.com/wp-content/themes/uxwing/download/02-arrow-direction/arrow-bottom.png") no-repeat 70% 50%;
  background-size: 50% 50%;
}

.select ul {
  width: 120px;
  border: 1px solid #999;
  position: absolute;
  background: #fff;
  border-top: none;
  margin: 1px 0 0 -1px;
  cursor: pointer;
}

.select ul li {
  padding: 8px 5px;
}

 

다음과 같은 모습이 될 것입니다.

 

styling을 1차적으로 한 상태

 

문제점이 있습니다. 텍스트의 길이가 긴 옵션에 대해 말줄임표로 깔끔하게 처리하겠습니다.

 

.select ul li,
.select .selected .selected-value {
  white-space: nowrap; /* 줄바꿈 안함 */
  overflow: hidden;
  text-overflow: ellipsis; /* 말줄임 적용 */
}

 

다음으로 옵션에 마우스를 올려놨을 때 배경색을 입혀보도록 하겠습니다.

 

.select ul li:hover {
  background: rgba(168, 156, 235, 0.35)
}

 

완성된 모습

 

여기까지 select 태그를 미니멀한 디자인으로 흉내를 내봤습니다. div 태그의 높은 자유도를 이용해 다양한 스타일링이 가능합니다. 따라서 정말 다양한 디자인을 구현해낼 수 있습니다. 아직 기능 구현이 남았습니다. 이제 select 태그처럼 기능을 하도록 만들기 위해 javascript 코드를 작성해야 합니다.

 

 

 

 

 

 

custom select 기능 구현하기

 

(포스팅 하단에 있는 코드펜으로 시현해볼 수 있습니다.)

 

일단 option은 처음엔 닫혀있는 상태를 유지해주기 위해 display: none을 추가해줍니다. 그리고 option이 열려있는 상태는 active라는 class name으로 관리하겠습니다.

 

.select ul {
  display: none;
}

.select.active ul {
  display: initial;
}

 

먼저 select를 누르면 active classname을 추가/제거해주는 함수를 만듭니다.

 

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

 

그리고 select click event에 toggleSelectBox를 설치해줍니다.

 

const selectBoxElements = document.querySelectorAll(".select");

selectBoxElements.forEach(selectBoxElement => {
  selectBoxElement.addEventListener("click", function (e) {
    toggleSelectBox(selectBoxElement);
  });
});

 

다음으로 option을 선택하면 해당 값을 선택하는 함수를 작성합니다.

 

function selectOption(optionElement) {
  const selectBox = optionElement.closest(".select");
  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);
  });
});

 

이제 option을 클릭하면 해당 값이 선택되며, 열렸다 닫히는 기능까지 추가된 select가 완성되었습니다.

하지만 뭔가 부족한 느낌이 듭니다. option이 펼쳐진 상태에서도 여백을 클릭해서 select를 닫는 기능을 추가하고 싶습니다. 이를 위해서는 document 객체에 click event handling을 추가합니다.

 

document.addEventListener("click", function (e) {
  const targetElement = e.target;
  const isSelect = targetElement.classList.contains("select") || targetElement.closest(".select");

  if (isSelect) {
    return;
  }

  const allSelectBoxElements = document.querySelectorAll(".select");

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

 

이제 끝났습니다. 완성된 모습은 다음과 같습니다.

 

 

See the Pen Untitled by cocoder16 (@cocoder16) on CodePen.

 

 

이와 같이 정말 최소한의 select기능만을 수행하는 커스텀 셀렉트를 만들어봤습니다. 그런데 여기에는 한계가 존재합니다. 해당 커스텀 셀렉트가 모듈화 되지 않았다는 것입니다. 재사용성을 높이고 사이드 이펙트를 없애기 위해서는 모듈화를 해줘야 합니다.

 

그런데 다행히도 우리는 이미 뛰어난 개발자들이 만든 좋은 라이브러리들을 사용할 수 있습니다. 이런 라이브러리들은 모듈화가 잘되어있고 사이드 이펙트가 없도록 꾸준히 관리되고 있으며 (관리되고 있는 라이브러리를 사용해야 합니다.) 또한 지금 포스팅에서 만든 커스텀 셀렉트보다 훨씬 더 많은 기능들을 지원하기도 합니다. "custom select library"와 같은 키워드로 검색하면 대중적인 라이브러리들을 찾아볼 수 있습니다.

 

 

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기