여러 개가 펼쳐지는 아코디언 리스트 구현하기

jinvicky·2024년 10월 8일
0
post-custom-banner

Intro


아코디언 리스트를 구현하는 방식은 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>
profile
경험하고 공부한 것들 풀어서 쓰는 것을 좋아합니다
post-custom-banner

0개의 댓글