<details>와 <summary> 태그로 아코디언 UI 만들기

작은 영역 안에 선택적으로 필요한 상세 내용을 펼쳐서 표시하는 배치 방식 중에 아코디언 UI가 있습니다.

아코디언처럼 접었다 폈다 하는 구조로 UI가 구성되어있고, 초기 상태에서는 주요 키워드(제목)만 표시되다가 키워드(제목)를 클릭하거나 선택하면 펼쳐지면서 해당 키워드에 해당하는 상세 내용이 보이는 구조입니다.

아코디언 UI 기능을 제공하는 라이브러리는 여러 가지가 있습니다.

대부분은 블록 태그와 자바스크립트로 구현한 구현체들입니다.

jQuery UI 아코디언 라이브러리 화면

사용자들의 요구가 있다보니 HTML 표준으로 아코디언 UI를 구현해주는 태그인 <details>와 <summary> 태그 2가지가 추가되었습니다.

기능면으로는 자바스크립트 도움 없이 기본적인 아코디언UI의 기본 기능을 구현할 수 있어서 좋기는 하지만, 기본 모양이 그렇게 보기 좋지는 않습니다. CSS로 장식적인 요소를 많이 추가해야 보기좋은 아코디언 UI를 만들 수 있습니다.

그리고, HTML과 CSS만으로 구현한 기본 아코디언UI는 선택한 항목만 펼쳐지고 나머지 다른 항목들은 모두 닫히도록 하는 기능은 지원하지 않기 때문에 자바스크립트의 도움을 약간 받아야 합니다.

자바스크립트로 아코디언 UI의 펼침 기능을 보완한 화면

아코디언 UI 기본 구조

아코디언 UI는 접었다 펼쳐지는 1개의 토글 아이템을 여러 개 나열한 것입니다.

접었다 펼쳐지는 아이템 1개를 만들 수 있으면 되는 단순한 구조로 되어 있습니다.

아코디언 UI 아이템 1개는 아이템을 감싸는 <details> 태그 1개와 대표로 표시하는 키워드(제목)를 위한 <summary> 태그 1개로 구성됩니다. <details> 태그는 <summary> 태그를 품고 있으며, <summary> 태그는 반드시 <details> 태그 바로 밑의 자식 요소로 위치해야 합니다. 손자 위치가 되면 키워드의 기능을 상실하고 일반 태그로 인식합니다.

<summary> 태그는 몇 가지 제약 사항이 있습니다.

  • details > summary 구조로 반드시 바로 하위 자식 요소로 와야 함
  • 1개만 있어야 함. 2개 이상 있을 경우 첫 번째 오는 <summary> 태그만 키워드로 인식되며, 나머지는 일반 태그로 처리됨.
  • <details> 태그의 자식 태그이면 위치는 무관함. 상세 내용을 표시하는 다른 태그들과 섞여 있어도 인식됨.
  • <details> 태그 안에 <summary> 태그가 없으면 "세부정보"로 기본 요약 키워드가 자동 표시됨.
  • <summary> 태그로 표시하는 키워드 앞에는 화살표 마커가 표시되며, 아코디언 UI 토글에 따라 화살표 방향이 변함.

기본 아코디언UI 태그는 다음과 같이 작성합니다.

<summary> 태그로 표시하는 키워드(타이틀)를 클릭하면 감추어져 있던 <details> 태그 하위의 나머지 모든 태그들이 표시되는 구조로 동작합니다. <details> 하위 태그는 <summary> 태그를 제외하면 콘텐츠를 표시하는 아무 태그나 올 수 있습니다.

<details>
    <summary>키워드(제목)</summary>
    <p>상세보기 표시 내용</p>
</details>

기본으로 펼쳐놓기

<details> 태그에는 사용할 수 있는 속성이 1개 있습니다.

기본으로 닫혀있는 아코디언UI를 펼침 상태로 표시되도록 하는 속성으로 "open" 속성이 있습니다. 별도 속성 값은 없고, 속성이 있는 것만으로 펼침 상태 표시가 됩니다.

<details open>
    <summary>키워드(제목)</summary>
    <p>상세보기 표시 내용</p>
</details>

자바스크립트로 아코디언UI 제어하기

아코디언 UI는 HTML 태그와 CSS 만으로 기본적인 동작과 사용이 가능합니다.

다만 고급스러운 동작이나 이벤트 처리를 하려면 자바스크립트의 도움을 어느 정도는 필요로 합니다.

자바스크립트를 이용해서 여러 개의 아코디언UI 중에서 1개의 아코디언 아이템만 펼쳐지도록 하는 이벤트 처리를 해보겠습니다.

먼저 아코디언UI 아이템의 키워드를 클릭해서 상세보기를 펼치거나 닫는 동작은 <details> 태그에 "toggle" 이벤트를 발생시킵니다.

자바스크립트로 "toggle" 이벤트가 발생하면 필요한 동작을 처리하는 이벤트 핸들러를 만들면 됩니다.

"toggle" 이벤트가 발생한 아코디언UI 아이템이 열린 동작이면 나머지 아코디언 UI 아이템 중에서 열린 아이템(들)의 "open" 속성을 삭제해서 닫는 처리를 합니다.

<summary> 태그에 클릭(click) 이벤트 핸들러를 붙이는 것이 아닙니다. 클릭 이벤트로 처리하면 더 복잡한 구현을 해야 합니다. 중요합니다!

window.addEventListener('DOMContentLoaded', function(){
    document.querySelectorAll('details').forEach(function(item){
        item.addEventListener("toggle", event => {
        let toggled = event.target;
        if (toggled.attributes.open) {/* 열었으면 */
          /* 나머지 다른 열린 아이템을 닫음 */
          document.querySelectorAll('details[open]').forEach(function(opened){
              if(toggled != opened) /* 현재 열려있는 요소가 아니면 */
                opened.removeAttribute('open'); /* 열림 속성 삭제 */
          });
        }
      })
    });
});
아코디언UI 1개만 펼쳐지는 화면

보기 좋은 아코디언UI 디자인 만들기

아코디언 UI에 아이템이 1개면 다음처럼 CSS로 디자인을 적용해서 보기 좋게 만들 수 있습니다.

        <details>
            <summary>기본 구조</summary>
            <p>상세내용</p>
        </details>

아코디언 UI를 클릭해서 상세 내용 보기가 열리면 <details> 태그에 "open" 속성이 추가됩니다. 이 속성을 이용해서 아코디언 UI가 열린 상태일 때 디자인을 다르게 적용할 수 있습니다.

"open" 속성의 상태 값은 없기 때문에 "details[open]" 과 같이 CSS 선택자를 사용하면 됩니다.

details {
    border: 1px solid #ccc;
    border-radius: 10px;
    padding: .5em .5em 0;
    margin-bottom: 1em;
}
summary {
    font-weight: bold;
    margin: -.5em -.5em 0;
    padding: .5em;
    cursor: pointer;
}
details[open] {
    padding: .5em;
}
details[open] summary {
    border-bottom: 1px solid #aaa;
    margin-bottom: .5em;
    background-color: #fff0c0;
    border-radius: 10px 10px 0 0;
}

앞서 만든 아이템 1개 아코디언을 활용해서 아코디언 아이템이 여러 개일 때 하나의 목록처럼 디자인된 구조를 만들어보겠습니다. 마찬가지로 앞서 만들었던 자바스크립트 코드를 활용해서 아코디언 아이템이 펼쳐지면 다른 펼쳐졌던 아이템들은 모두 닫히도록 합니다.

먼저 여러 개의 아코디언 아이템들을 하나로 묶어야 하므로 블록 태그로 여러 개이 아코디언 아이템들을 감쌉니다. 목록 디자인 적용을 위해 "multi-accordion" 클래스를 하나 추가합니다.

      <div class="multi-accordion">
        <details>
            <summary>기본 구조</summary>
            <p>상세내용</p>
        </details>
        <details>
            <summary>마커(불릿) 변경</summary>
            <p>상세내용</p>
        </details>
        <details>
            <summary>자바스크립트 제어</summary>
            <p>상세내용</p>
        </details>
      </div>

디자인 요소가 없으면 다음처럼 밋밋하고 목록형이라고 보기 어려운 모양인 아코디언UI 목록이 표시됩니다.

디자인 요소 없는 기본 아코디언 목록

CSS를 다음처럼 작성해서 아코디언UI 목록을 만듭니다.

.multi-accordion {
    border: 1px solid #ccc;
    border-radius: 10px;
    padding: .5em .5em 0;
    overflow: hidden;
}
.multi-accordion summary {
    font-weight: bold;
    margin: -.5em -.5em 0;
    padding: .5em;
    cursor: pointer;
}
/* 둥근 테두리인 첫 번째 아이템 상단의 배경색이 삐져 나오는 것 처리 */
.multi-accordion details:first-child summary{
    border-radius: 10px 10px 0 0;
}
/* 열림 아이템 */
.multi-accordion details[open] summary {
    border-bottom: 1px solid #aaa;
    margin-bottom: .5em;
    background-color: #fff0c0;
}
/* 열린 아이템이 첫 번째 아이템이 아니면 디자인 요소 추가 */
.multi-accordion details:not(:first-child)[open] summary {
    margin-top: .15em;
    border-top: 1px solid #aaa;
}
/* 열림 아이템 다음 아이템의 키워드 상단에 테두리선 추가 */
.multi-accordion details[open] + details > summary {
    border-top: 1px solid #aaa;
}
완성된 멀티 아이템 아코디언UI

완성된 아코디언UI 소스를 다음 링크를 클릭해서 다운로드할 수 있습니다.


마커(불릿) 모양 변경하기

아코디언UI 아이템 앞에 자동으로 붙는 화살표 마커는 가상 요소로 구현한 디자인입니다.

개발자 도구로 확인하면 다음처럼 마커 가상 요소가 <summary> 태그에 자동으로 붙는 것을 볼 수 있습니다.

마커 가상 요소로 붙었다는 것은 <summary> 태그에 마커 가상 요소를 재정의해서 다른 마커로 변경할 수 있다는 뜻입니다.

기본 화살표 마커처럼 아코디언 UI가 토글 되는 데 따라 화살표 방향이 자동으로 바뀌는 기능을 커스텀 마커로 구현해보겠습니다.

조금 단순하게 "+ "로 표시되는 마커가 토글되면 "- "로 바뀌도록 해보겠습니다.

"content" 속성으로 화살표를 대신하는 문자나 아이콘 등을 대체할 수 있습니다. 문자를 사용할 때는 글꼴을 고정 간격 글자인 "monospace"로 해야 가변 글꼴로 인해 키워드 문자열이 밀려 보이는 현상이 생기지 않으므로 주의해야 합니다.

summary::marker{
    content: "+ ";
    font-family: monospace;
}
details[open] summary::marker {
    content: "- ";
}
마커 화살표를 +/- 문자로 변경

마커를 사용하지 않아서 감추고 싶으면 다음처럼 CSS로 마커를 감출 수 있습니다.

summary::marker {
    display: none;
    content: "";
}

마커 화살표 애니메이션 추가

아코디언UI 키워드 왼쪽에 표시되는 기본 화살표 마커는 "::marker" 가상 요소로 구현된 것입니다.

기본 제공되는 화살표 기능은 펼쳐지면서 마커 화살표 모양이 바뀌기 때문에 애니메이션 효과를 적용할 수 없습니다.

기본 마커를 대신해서 "::before" 가상 요소로 새 마커 화살표를 붙이면 상세보기가 펼쳐질 때 90도 회전하는 애니메이션을 추가할 수 있습니다.

다음 CSS는 테두리선을 이용해 삼각형 모양 화살표 디자인을 만들고, 만든 화살표를 90도 회전시키는 애니메이션입니다.

details summary::marker {
  display:none;
  content: '';
}
details[open] > summary:before {
  transform: rotate(90deg);
}
summary:before {
  content: '';
  border-width: .4rem;
  border-style: solid;
  border-color: transparent transparent transparent #fff;
  position: absolute;
  top: 1.3rem;
  left: 1rem;
  transform: rotate(0);
  transform-origin: .2rem 50%;
  transition: .25s transform ease;
}