아코디언 리스트를 구현하는 방식은 2가지가 있다.
1. 단일 탭 방식
클릭한 아코디언만 펼쳐지고, 나머지는 닫힌다.
2. 다중 탭 방식
기존 펼쳐진 아코디언들은 유지하고, 클릭한 아코디언을 추가로 펼친다.
1번의 경우 useState로 tabIndex등을 선언해서 단일 관리할 수 있다. (vue3의 경우 v-show 조건을 인덱스 비교로 할 수 있지)
2번의 경우 어떻게 해결할지 좀 더 고민이 된다.
이번에는 2번의 경우를 css와 event 조작으로 처리하는 방법을 소개하겠다.
주요 함수인 toggleEvent
function toggleEvent(event) {
let parent = event.currentTarget.closest('li');
if (parent !== null) {
let bottom = parent.querySelector('.bottomMenu');
let ul = null;
if (bottom !== null) {
ul = bottom.querySelector(':scope > ul');
}
if (ul !== null) {
let bottomHeight = ul.clientHeight + 'px';
console.log('cal bottomHeight:', bottomHeight);
if (parent.classList.contains('opened')) {
parent.classList.remove('opened');
bottomHeight = '0';
} else {
parent.classList.add('opened');
}
if (bottom !== null) {
bottom.style.height = bottomHeight;
}
}
}
}
ai로 테스트 환경 html을 만들었다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아코디언 테스트</title>
<style>
.bottomMenu {
overflow: hidden;
transition: height 0.3s ease;
height: 0; /* 초기 높이를 0으로 설정 */
}
.opened .bottomMenu {
height: auto; /* 열린 상태에서는 자동으로 높이 설정 */
}
.list-item {
margin: 10px 0;
}
.title {
cursor: pointer;
background-color: #007bff;
color: white;
padding: 10px;
border: none;
display: block;
width: 100%;
text-align: left;
}
</style>
</head>
<body>
<ul>
<li class="list-item">
<button type="button" class="title" onclick="toggleEvent(event)">항목 1</button>
<div class="bottomMenu">
<ul>
<li>하위 항목 1-1</li>
<li>하위 항목 1-2</li>
</ul>
</div>
</li>
<li class="list-item">
<button type="button" class="title" onclick="toggleEvent(event)">항목 2</button>
<div class="bottomMenu">
<ul>
<li>하위 항목 2-1</li>
<li>하위 항목 2-2</li>
</ul>
</div>
</li>
<li class="list-item">
<button type="button" class="title" onclick="toggleEvent(event)">항목 3</button>
<div class="bottomMenu">
<ul>
<li>하위 항목 3-1</li>
<li>하위 항목 3-2</li>
</ul>
</div>
</li>
</ul>
<script>
function toggleEvent(event) {
let parent = event.currentTarget.closest('li');
if (parent !== null) {
let bottom = parent.querySelector('.bottomMenu');
let ul = null;
if (bottom !== null) {
ul = bottom.querySelector(':scope > ul');
}
if (ul !== null) {
let bottomHeight = ul.clientHeight + 'px';
console.log('cal bottomHeight:', bottomHeight);
if (parent.classList.contains('opened')) {
parent.classList.remove('opened');
bottomHeight = '0';
} else {
parent.classList.add('opened');
}
if (bottom !== null) {
bottom.style.height = bottomHeight;
}
}
}
}
</script>
</body>
</html>