웹 서핑을 하면서 드롭다운 메뉴를 사용해 본 적이 있을 것 입니다. 이는 주로 Form 양식에서 사용자 입력 수집하기, 웹 애플리케이션에서 탐색 메뉴 구현 등에 사용됩니다.
드롭다운은 애플리케이션의 레이아웃 흐름을 깨지 않고 여러 기능을 제공하는 가장 좋은 방법입니다. 웹 외에도 exe 프로그램, 운영체제 등에서 사용됩니다.
이 포스트에서는 HTML, CSS, 자바스크립트를 사용하여 드롭다운 탐색 메뉴를 만드는 방법을 알아보겠습니다. 다음은 빌드할 내용의 스크린샷입니다.
Table of Contents
Step 1 – 드롭다운 마크업 추가
시각적 요소를 위해 이번 예제에서는 아이콘을 사용하겠습니다. 아이콘은 간단히 처리하기 위해 Boxicons라는 무료 라이브러리를 사용 하겠습니다. 원하는 다른 대안이 있으면 자유롭게 선택할 수 있습니다.
페이지에 Boxicons를 설정하는 방법은 여러가지가 있습니다. 가장 간단한 방법은 다음과 같이 HTML 파일의 <head>에 태그를 정의하는 것 입니다:
<head>
<link href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet" />
</head>
아이콘을 추가한 후 “container”라는 클래스 명을 가지는 div 요소를 추가합니다. 이 요소 안에 버튼과 드롭다운 메뉴를 넣을 것 입니다.
container 내부에 button 요소를 생성하고 클래스 명과 id를 “btn”으로 지정합니다. 버튼의 텍스트로 “Dropdown”과 화살표 아이콘을 지정합니다.
다음은 버튼에 대한 마크업입니다:
<button class="btn" id="btn">
Dropdown
<i class="bx bx-chevron-down" id="arrow"></i>
</button>
다음으로 드롭다운 메뉴 자체에 대한 마크업을 추가하겠습니다.
button 태그 다음에 div 요소를 추가하고 클래스 명과 id를 “dropdown”으로 지정합니다. 이 요소 내에는 각각의 메뉴 항목에 대해 a 태그를 추가하고 해당 텍스트와 아이콘을 지정합니다.
<div class="dropdown" id="dropdown">
<a href="#create">
<i class="bx bx-plus-circle"></i>
Create New
</a>
<a href="#draft">
<i class="bx bx-book"></i>
All Drafts
</a>
<a href="#move">
<i class="bx bx-folder"></i>
Move To
</a>
<a href="#profile">
<i class="bx bx-user"></i>
Profile Settings
</a>
<a href="#notification">
<i class="bx bx-bell"></i>
Notification
</a>
<a href="#settings">
<i class="bx bx-cog"></i>
Settings
</a>
</div>
위의 HTML 코드를 모두 index.html 파일에 작성하고 실행하면 다음과 같은 화면을 볼 수 있습니다:
아직 좋게 보이진 않습니다. 이제 메뉴에 스타일링을 해보겠습니다.
Step 2 – 드롭다운 메뉴 스타일링
먼저 페이지 모든 요소의 기본 margin과 padding을 재설정하고 CSS 파일 전체에서 재사용할 수 있도록 변수를 설정합니다.
@import url(https://fonts.googleapis.com/css?family=Inter:100,200,300,regular,500,600,700,800,900);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Inter", sans-serif;
--shadow: rgba(0, 0, 0, 0.05) 0px 6px 10px 0px, rgba(0, 0, 0, 0.1) 0px 0px 0px 1px;
--color: #166e67;
--gap: 0.5rem;
--radius: 5px;
}
body {
margin: 2rem;
background-color: #b3e6f4;
font-size: 0.9rem;
color: black;
}
다음 단계는 버튼과 드롭다운 컨테이너 자체의 스타일을 지정하는 것 입니다.
.btn {
background-color: white;
display: flex;
align-items: center;
justify-content: flex-start;
column-gap: var(--gap);
padding: 0.6rem;
cursor: pointer;
border-radius: var(--radius);
border: none;
box-shadow: var(--shadow);
position: relative;
}
.bx {
font-size: 1.1rem;
}
.dropdown {
position: absolute;
width: 250px;
box-shadow: var(--shadow);
border-radius: var(--radius);
margin-top: 0.3rem;
background: white;
}
.dropdown a {
display: flex;
align-items: center;
column-gap: var(--gap);
padding: 0.8rem 1rem;
text-decoration: none;
color: black;
}
.dropdown a:hover {
background-color: var(--color);
color: white;
}
드롭다운 요소는 버튼 위에 배치되므로 버튼의 position
은 relative
이고 드롭다운의 position
은 absolute
가 됩니다.
이렇게 하면 페이지 레이아웃 흐름에 영향을 주지 않으면서 두 요소를 가깝게 또는 겹치게 배치할 수 있습니다.
다음은 결과물입니다:
이제 스타일을 지정했으므로 처음 부터 메뉴가 표시되지 않고 버튼을 눌렀을 때만 표시되도록 하겠습니다. 이러한 작업을 위해 CSS에 메뉴 숨기기 속성을 추가하겠습니다.
CSS의 숨기기는 display: none
을 사용하면 되는데 MDN Docs에 의하면 이는 애니메이션 가능하지 않습니다.
여기서는 애니메이션이 가능하면서 메뉴를 숨기기위해 visibility
와 opacity
를 사용해 보겠습니다. 앞서 정의했던 dropdown 클래스에 visibility: hidden
과 opacity: 0
을 추가해 페이지에서 메뉴를 숨깁니다.
메뉴가 보이는 상황을 처리하기 위해 show 클래스를 추가하고 visibility: visible
과 opacity: 1
을 설정합니다. 이 클래스는 자바스크립트를 사용하여 요소에 삽입할 수 있습니다.
.dropdown {
position: absolute;
width: 250px;
box-shadow: var(--shadow);
border-radius: var(--radius);
margin-top: 0.3rem;
background: white;
transition: all 0.1s cubic-bezier(0.16, 1, 0.5, 1);
transform: translateY(0.5rem);
visibility: hidden;
opacity: 0;
}
.show {
transform: translateY(0rem);
visibility: visible;
opacity: 1;
}
.arrow {
transform: rotate(180deg);
transition: 0.2s ease;
}
메뉴를 숨기고 보이는 스타일과 함께 버튼을 누르면 화살표 아이콘을 회전하는 arrow 클래스도 추가했습니다.
Step 3 – 드롭다운 기능 추가
우선 재사용이 가능하도록 getElementById() 메소드를 사용하여 조작할 요소들을 변수에 저장하겠습니다.
const dropdownBtn = document.getElementById("btn");
const dropdownMenu = document.getElementById("dropdown");
const toggleArrow = document.getElementById("arrow");
다음으로 show와 arrow 클래스를 토글하는 toggleDropdown() 함수를 만들겠습니다.
const toggleDropdown = function () {
dropdownMenu.classList.toggle("show");
toggleArrow.classList.toggle("arrow");
};
그런 다음 addEventListener 메소드를 사용하여 버튼을 클릭하면 위의 함수를 호출하게 합니다. 이렇게 하면 버튼을 클릭할 때마다 메뉴 표시 및 숨기기 기능이 실행됩니다.
dropdownBtn.addEventListener("click", function (e) {
e.stopPropagation();
toggleDropdown();
});
이벤트 핸들러 내에 stopPropagation() 메소드를 호출한 것을 확인할 수 있습니다. 이렇게 하면 클릭 이벤트가 부모 요소로 전달되는 것을 방지하여 기능이 두 번 실행되는 것을 방지합니다.
다른 DOM 요소 클릭 시 드롭다운 메뉴 닫기
드롭다운 메뉴는 일반적으로 다음 네 가지 방법으로 닫힙니다:
- 메뉴를 열었던 버튼을 다시 누를 때
- 메뉴의 항목을 눌렀을 때
- body 요소 같은 메뉴의 바깥을 눌렀을 때
- esc 키를 눌렀을 때
여기서는 세 번째 경우 까지만 다루겠습니다.
루트 요소인 <html>을 참조하기 위해 document.documentElement
를 사용합니다. 여기에 클릭 이벤트를 추가해 toggleDropdown() 함수를 실행합니다.
주의할 점은 메뉴 요소에 show 클래스가 존재하는지 확인을 하고 toggleDropdown() 함수를 실행해야 한다는 점 입니다.
document.documentElement.addEventListener("click", function () {
if (dropdownMenu.classList.contains("show")) {
toggleDropdown();
}
});
See the Pen Untitled by shinyks (@shinyks) on CodePen.
전체 코드
index.html
<html>
<head>
<link href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<button class="btn" id="btn">
Dropdown
<i class="bx bx-chevron-down" id="arrow"></i>
</button>
<div class="dropdown" id="dropdown">
<a href="#create">
<i class="bx bx-plus-circle"></i>
Create New
</a>
<a href="#draft">
<i class="bx bx-book"></i>
All Drafts
</a>
<a href="#move">
<i class="bx bx-folder"></i>
Move To
</a>
<a href="#profile">
<i class="bx bx-user"></i>
Profile Settings
</a>
<a href="#notification">
<i class="bx bx-bell"></i>
Notification
</a>
<a href="#settings">
<i class="bx bx-cog"></i>
Settings
</a>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
style.css
@import url(https://fonts.googleapis.com/css?family=Inter:100,200,300,regular,500,600,700,800,900);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Inter", sans-serif;
--shadow: rgba(0, 0, 0, 0.05) 0px 6px 10px 0px,
rgba(0, 0, 0, 0.1) 0px 0px 0px 1px;
--color: #166e67;
--gap: 0.5rem;
--radius: 5px;
}
body {
margin: 2rem;
background-color: #b3e6f4;
font-size: 0.9rem;
color: black;
}
.btn {
background-color: white;
display: flex;
align-items: center;
justify-content: flex-start;
column-gap: var(--gap);
padding: 0.6rem;
cursor: pointer;
border-radius: var(--radius);
border: none;
box-shadow: var(--shadow);
position: relative;
}
.bx {
font-size: 1.1rem;
}
.dropdown {
position: absolute;
width: 250px;
box-shadow: var(--shadow);
border-radius: var(--radius);
margin-top: 0.3rem;
background: white;
visibility: hidden;
opacity: 0;
transform: translateY(0.5rem);
transition: all 0.1s cubic-bezier(0.16, 1, 0.5, 1);
}
.dropdown a {
display: flex;
align-items: center;
column-gap: var(--gap);
padding: 0.8rem 1rem;
text-decoration: none;
color: black;
}
.dropdown a:hover {
background-color: var(--color);
color: white;
}
.show {
visibility: visible;
opacity: 1;
transform: translateY(0rem);
}
.arrow {
transform: rotate(180deg);
transition: 0.2s ease;
}
script.js
const dropdownBtn = document.getElementById("btn");
const dropdownMenu = document.getElementById("dropdown");
const toggleArrow = document.getElementById("arrow");
// Toggle dropdown function
const toggleDropdown = function () {
dropdownMenu.classList.toggle("show");
toggleArrow.classList.toggle("arrow");
};
// Toggle dropdown open/close when dropdown button is clicked
dropdownBtn.addEventListener("click", function (e) {
e.stopPropagation();
toggleDropdown();
});
// Close dropdown when dom element is clicked
document.documentElement.addEventListener("click", function () {
if (dropdownMenu.classList.contains("show")) {
toggleDropdown();
}
});