Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

CSS와 JavaScript로 커스텀 셀렉트 박스 만드는 방법

HTML의 기본 <select> 요소는 브라우저마다 디자인이 다르게 표시되고 스타일을 자유롭게 변경하기 어렵습니다. 이럴 때 CSS와 JavaScript를 활용하면 사이트 디자인에 어울리는 커스텀 셀렉트 박스를 직접 만들 수 있습니다.

기본 원리는 간단합니다. 원래 셀렉트 요소는 화면에서 숨기고(display: none;), 대신 새로 만든 DIV 요소로 드롭다운 UI를 구성한 뒤 클릭 이벤트를 연결하는 방식입니다.

전체 예제 코드

아래 코드는 순수 HTML, CSS, JavaScript만으로 동작하는 커스텀 셀렉트 박스의 완전한 예제입니다.

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
    .customSelect {
        position: relative;
        font-family: Arial, Helvetica, sans-serif;
    }
    .customSelect select {
        display: none;
    }
    .select-selected {
        background-color: rgb(78, 11, 122);
    }
    .select-selected:after {
        position: absolute;
        content: "";
        top: 14px;
        right: 10px;
        width: 0;
        height: 0;
        border: 6px solid transparent;
        border-color: #fff transparent transparent transparent;
    }
    .select-selected.select-arrow-active:after {
        border-color: transparent transparent #fff transparent;
        top: 7px;
    }
    .select-items div, .select-selected {
        color: #ffffff;
        padding: 8px 16px;
        border: 1px solid transparent;
        cursor: pointer;
        user-select: none;
    }
    .select-items {
        position: absolute;
        background-color: rgb(138, 29, 148);
        top: 100%;
        left: 0;
        right: 0;
        z-index: 99;
    }
    .select-hide {
        display: none;
    }
    .select-items div:hover, .sameSelected {
        background-color: rgba(0, 0, 0, 0.1);
    }
</style>
</head>
<body>
<h1>Custom Select Example</h1>
<div class="customSelect" style="width:200px;">
<select>
<option value="0">Select Animal:</option>
<option value="Giraffe">Giraffe</option>
<option value="Lion">Lion</option>
<option value="Cow">Cow</option>
<option value="Dog">Dog</option>
<option value="Tiger">Tiger</option>
</select>
</div>
<script>
    var customSelectEle, i, j, selElmnt, divEle, divEleSelected, c;
    customSelectEle = document.querySelector(".customSelect");
    selElmnt = customSelectEle.getElementsByTagName("select")[0];
    divEle = document.createElement("DIV");
    divEle.setAttribute("class", "select-selected");
    divEle.innerHTML = selElmnt.options[selElmnt.selectedIndex].innerHTML;
    customSelectEle.appendChild(divEle);
    divEleSelected = document.createElement("DIV");
    divEleSelected.setAttribute("class", "select-items select-hide");
    Array.from(selElmnt).forEach((item, index) => {
        c = document.createElement("DIV");
        c.innerHTML = selElmnt.options[index].innerHTML;
        c.addEventListener("click", function(e) {
            var y, i, k, selEleParent, selEleSibling;
            selEleParent = this.parentNode.parentNode.getElementsByTagName( "select" )[0];
            selEleSibling = this.parentNode.previousSibling;
            for (i = 0; i < selEleParent.length; i++) {
                if (selEleParent.options[i].innerHTML == this.innerHTML) {
                    selEleParent.selectedIndex = i;
                    selEleSibling.innerHTML = this.innerHTML;
                    y = this.parentNode.getElementsByClassName("sameSelected");
                    for (k = 0; k < y.length; k++) {
                        y[k].removeAttribute("class");
                    }
                    this.setAttribute("class", "sameSelected");
                    break;
                }
            }
            selEleSibling.click();
        });
        divEleSelected.appendChild(c);
    });
    customSelectEle.appendChild(divEleSelected);
    divEle.addEventListener("click", function(e) {
        e.stopPropagation();
        closeSelect(this);
        this.nextSibling.classList.toggle("select-hide");
        this.classList.toggle("select-arrow-active");
    });
    function closeSelect(elmnt) {
        var customSelectEle,
        y,
        i,
        arrNo = [];
        customSelectEle = document.getElementsByClassName("select-items");
        y = document.getElementsByClassName("select-selected");
        for (i = 0; i < y.length; i++) {
            if (elmnt == y[i]) {
                arrNo.push(i);
            }
            else {
                y[i].classList.remove("select-arrow-active");
            }
        }
        for (i = 0; i < customSelectEle.length; i++) {
            if (arrNo.indexOf(i)) {
                customSelectEle[i].classList.add("select-hide");
            }
        }
    }
    document.addEventListener("click", closeSelect);
</script>
</body>
</html>

실행 결과

위 코드를 실행하면 아래와 같이 보라색 배경의 커스텀 셀렉트 박스가 화면에 나타납니다.

CSS와 JavaScript로 커스텀 셀렉트 박스 만드는 방법

셀렉트 박스를 클릭하면 화살표가 위쪽으로 바뀌면서 옵션 목록이 펼쳐집니다.

CSS와 JavaScript로 커스텀 셀렉트 박스 만드는 방법

코드 핵심 로직 살펴보기

1. 기본 셀렉트 숨기기

CSS에서 .customSelect select { display: none; } 규칙으로 원래 셀렉트 요소를 감춥니다. 이후 모든 UI는 JavaScript가 생성하는 DIV 요소로 대체됩니다.

2. 선택된 값 표시 영역 생성

.select-selected 클래스를 가진 DIV를 만들어 현재 선택된 옵션의 텍스트를 표시합니다. 이 요소에는 :after 가상 요소를 이용해 삼각형 화살표를 추가하고, 목록이 열릴 때는 .select-arrow-active 클래스가 붙어 화살표 방향이 뒤집힙니다.

3. 옵션 목록 생성과 클릭 처리

원래 셀렉트의 각 옵션을 순회하며 같은 텍스트를 가진 DIV를 만들고, 클릭 시 원본 셀렉트의 selectedIndex 값을 실제로 변경합니다. 덕분에 폼 제출 시에도 정상적으로 값이 전달됩니다. 선택된 옵션에는 .sameSelected 클래스를 부여해 현재 선택 항목을 시각적으로 강조합니다.

4. 외부 클릭 시 닫기

closeSelect() 함수는 문서 전체에 클릭 리스너로 등록되어 있어, 셀렉트 바깥 영역을 클릭하면 열려 있는 모든 드롭다운이 자동으로 닫힙니다. 반대로 셀렉트 자체를 클릭할 때는 e.stopPropagation()으로 이벤트 전파를 막아 목록이 유지되도록 처리했습니다.

마무리

이처럼 CSS와 JavaScript만으로도 브라우저 기본 셀렉트 박스를 완전히 대체하는 커스텀 드롭다운을 만들 수 있습니다. 색상, 패딩, 폰트 등 CSS 변수만 수정하면 프로젝트 디자인에 맞게 손쉽게 확장할 수 있으며, 여러 개의 셀렉트 박스에도 그대로 적용할 수 있습니다.