SOITZ

VanilaJS 로 아코디언 메뉴 구현하기

Published on

아코디언 메뉴는 웹사이트에서 정보를 효과적으로 표시하고 사용자 경험을 향상시키는 데 자주 사용됩니다. 이번 글에서는 VanillaJS만을 사용하여 간단한 아코디언 메뉴를 구현하는 방법을 설명하겠습니다. 별도의 라이브러리나 프레임워크 없이 순수 자바스크립트만으로 구현할 수 있습니다.

accordion

시작하기

먼저, 아코디언 메뉴를 포함할 HTML 구조를 만들어봅니다. 각 아코디언 항목인 .accordion-item에는 제목이 들어갈 button과 내용이 들어갈 section으로 구분됩니다.

HTML

<div id="accordion">
  <div class="accordion-item">
    <button>Section 1</button>
    <section>
      <p>내용</p>
    </section>
  </div>
</div>

CSS 스타일링

아코디언의 기본 스타일을 CSS로 정의해보겠습니다. 기본적으로, 내용은 숨겨져 있어야 하므로 section에 height:0을 주었습니다. 아코디언이 열린 상태.active이면 배경색과 글자색을 변경합니다.

.accordion-item button {
  width: 100%;
  padding: 10px;
  margin: 0;
  border: none;
  text-align: left;
  font-weight: bold;
  font-size: 18px;
  background-color: #eee;
  cursor: pointer;
  transition:
    background-color 0.2s ease-in-out,
    color 0.2s ease-in-out;
}
.accordion-item section {
  border-bottom: 1px solid #ccc;
  height: 0;
  overflow: hidden;
  transition: height 0.4s ease;
}
.accordion-item section p {
  padding: 10px;
}
.accordion-item.active button {
  background-color: #333;
  color: #efefef;
}

자바스크립트로 동작 추가하기

이제 VanillaJS를 사용하여 아코디언 메뉴의 동작을 구현해보겠습니다. 아코디언 제목button을 클릭하면 section의 높이를 지정합니다. 그리고 브라우저 사이즈 변경 시 열려있는 아코디언 아이템의 section의 높이를 새로 지정해줍니다.

// 아코디언 전체
const accordion = document.getElementById('accordion');

// 아코디언 클릭 트리거
const triggers = accordion.querySelectorAll('.accordion-item button');

// 트리거에 클릭 이벤트 추가
for (let i = 0; i < triggers.length; i++) {
  triggers[i].addEventListener('click', toggleTrigger);
}

function toggleTrigger() {
  const item = this.parentNode;
  const section = this.nextElementSibling;
  const p = section.querySelector('p');

  if (item.classList.contains('active')) {
    section.style.height = 0 + 'px';
  } else {
    section.style.height = p.clientHeight + 'px'; // section의 높이 지정
  }
  item.classList.toggle('active');
}

// 화면 사이즈 변경시 열려 있는 아코디언 아이템의 높이 조절
window.addEventListener('resize', () => {
  const items = accordion.querySelectorAll('.accordion-item ');
  items.forEach((item) => {
    // 활성화된 아코디언이면 텍스트의 높이를 가져와서 section의 높이에 지정
    if (item.classList.contains('active')) {
      const section = item.querySelector('section');
      const p = section.querySelector('p');
      section.style.height = p.clientHeight + 'px';
    }
  });
});

전체 코드

아래는 전체 코드입니다.

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="initial-scale=1.0, minimum-scale=1.0, width=device-width, viewport-fit=cover"
    />
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      html,
      body {
        height: 100%;
      }

      .accordion-item button {
        width: 100%;
        padding: 10px;
        margin: 0;
        border: none;
        text-align: left;
        font-weight: bold;
        font-size: 18px;
        background-color: #eee;
        cursor: pointer;
        transition:
          background-color 0.2s ease-in-out,
          color 0.2s ease-in-out;
      }
      .accordion-item section {
        border-bottom: 1px solid #ccc;
        height: 0;
        overflow: hidden;
        transition: height 0.4s ease;
      }
      .accordion-item section p {
        padding: 10px;
      }
      .accordion-item.active button {
        background-color: #333;
        color: #efefef;
      }
    </style>
    <title>VanilaJS Accordion</title>
  </head>

  <body>
    <div id="accordion">
      <div class="accordion-item">
        <button>Section 1</button>
        <section>
          <p>
            Lorem Ipsum is simply dummy text of the printing and typesetting
            industry. Lorem Ipsum has been the industry's standard dummy text
            ever since the 1500s, when an unknown printer took a galley of type
            and scrambled it to make a type specimen book. It has survived not
            only five centuries, but also the leap into electronic typesetting,
            remaining essentially unchanged.
          </p>
        </section>
      </div>
      <div class="accordion-item">
        <button>Section 2</button>
        <section>
          <p>
            Lorem Ipsum is simply dummy text of the printing and typesetting
            industry. Lorem Ipsum has been the industry's standard dummy text
            ever since the 1500s, when an unknown printer took a galley of type
            and scrambled it to make a type specimen book. It has survived not
            only five centuries, but also the leap into electronic typesetting,
            remaining essentially unchanged. It was popularised in the 1960s
            with the release of Letraset sheets containing Lorem Ipsum passages,
            and more recently with desktop publishing software like Aldus
            PageMaker including versions of Lorem Ipsum.
          </p>
        </section>
      </div>
      <div class="accordion-item">
        <button>Section 3</button>
        <section>
          <p>
            Lorem Ipsum is simply dummy text of the printing and typesetting
            industry. Lorem Ipsum has been the industry's standard dummy text
            ever since the 1500s, when an unknown printer took a galley of type
            and scrambled it to make a type specimen book.
          </p>
        </section>
      </div>
    </div>
    <script>
      // 아코디언 전체
      const accordion = document.getElementById('accordion');

      // 아코디언 클릭 트리거
      const triggers = accordion.querySelectorAll('.accordion-item button');

      // 트리거에 클릭 이벤트 추가
      for (let i = 0; i < triggers.length; i++) {
        triggers[i].addEventListener('click', toggleTrigger);
      }

      function toggleTrigger() {
        const item = this.parentNode;
        const section = this.nextElementSibling;
        const p = section.querySelector('p');

        if (item.classList.contains('active')) {
          section.style.height = 0 + 'px';
        } else {
          section.style.height = p.clientHeight + 'px'; // section의 높이 지정
        }
        item.classList.toggle('active');
      }

      // 화면 사이즈 변경시 열려 있는 아코디언 아이템의 높이 조절
      window.addEventListener('resize', () => {
        const items = accordion.querySelectorAll('.accordion-item ');
        items.forEach((item) => {
          // 활성화된 아코디언이면 텍스트의 높이를 가져와서 section의 높이에 지정
          if (item.classList.contains('active')) {
            const section = item.querySelector('section');
            const p = section.querySelector('p');
            section.style.height = p.clientHeight + 'px';
          }
        });
      });
    </script>
  </body>
</html>