아코디언 메뉴(Accordion Menu)란 컨텐츠를 담는 방식 중 하나로 네비게이션을 클릭하면 컨텐츠를 담은 컨테이너가 열리고 다른 네비게이션을 클릭하면 열려있는 컨테이너는 닫히고 클릭한 컨테이너가 열리는 구조이다.
이번 포스팅에서는 jQuery 메서드인 slideUp()
과 slideDown()
을 사용하여 아코디언 메뉴를 구현하고 효율적인 코드작성을 위해 관련 선택자를 필터링하는 메서드를 학습한다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>아코디언</title>
<meta name="description" content="아코디언" />
<link rel="stylesheet" href="./css/initial.min.css" />
<link rel="stylesheet" href="./css/style.css" />
<script type="text/javascript" src="./js/jquery_3.5.1.js"></script>
<script type="text/javascript" src="./js/common.js"></script>
</head>
<body>
<dl>
<dt>시험안내</dt>
<dd>
<ul>
<li>2021년 제3회 임기제공무원 경력경쟁임용시험 최종합격</li>
<li>
2022학년도 서울특별시 공립 유치원 선정경쟁시험 사전예고 연기 안내
</li>
<li>2021년도 제1회 초졸·중졸·고졸 검정고시 합격자 발표 공고</li>
<li>
2021년 서울특별시교육청 지방공무원(운전직렬 9급) 시행계획 공고
</li>
</ul>
</dd>
<dt>보도/설명 자료</dt>
<dd>
<ul>
<li>보직교사 수당 인상 제안</li>
<li>[참고]21.5.14.(금) 코로나19 대책 관련 일일 브리핑 자료</li>
<li>[입장문]중앙고 이대부고 판결에 대한 서울시교육청 입장</li>
<li>2021 신규교사 성장지원을 위한 멘토 운영</li>
</ul>
</dd>
<dt>더보기</dt>
<dd>
<ul>
<li>기타1</li>
<li>기타2</li>
<li>기타3</li>
<li>기타4</li>
</ul>
</dd>
</dl>
</body>
</html>
body안에 dl > dt + dd > ul > li 구조로 넣어주었고 탭을 총 3개 만들고 싶으므로 같은 구조로 3개를 만들어주면 된다.
(텍스트는 서울시청 홈페이지에서 임의로 가져왔다!)
dl, dt, dd를 쓸 일이 잘 없어서 헷갈리게 되는데 각각 무엇의 약자인지 생각하면 이해하기 쉽다!
- dl : Description-List
- dt : Description-Term
- dd : Description-Description
@charset "UTF-8";
html,
body {
height: 100%;
}
body {
font-family: 'Noto Sans KR', sans-serif;
color: #666666;
}
ul,
ol {
list-style: none;
}
dl {
width: 600px;
margin: 50px auto;
}
dt {
font-size: 18px;
height: 50px;
line-height: 50px;
text-indent: 30px;
color: #fff;
background: black;
cursor: pointer;
}
body > dl > dt:first-child {
background: rgb(50, 192, 145);
}
body > dl > dt:nth-child(3) {
background: rgb(126, 126, 126);
}
dd {
padding: 30px;
background: #d4d0c8;
}
dd ul li {
padding-bottom: 10px;
}
컨텐츠가 들어간 박스형태가 되도록 스타일링한다. 교육청 홈페이지에서 컨텐츠를 가져왔기 때문에 정보를 전달하기 쉽게 잔잔한 색으로 입혀주었다!☺️
$('dd:not(:first)').css('display', 'none');
본격적으로 로직을 짜볼 차례이다.
먼저 아코디언 메뉴의 첫 화면을 생각해보면 첫번째 컨테이너 이외의 모든 컨테이너 메뉴는 닫혀있다. 이 상태를 구현하기 위해 첫번째 dd
이외의 dd
는 모두 dislplay:none
을 주어 숨겨주었다.
이 때 선택자는 두번째, 세번째 dd를 선택하는 방식도 있지만 요소를 하나만 선택하는 것이 효율적이기 때문에 :not() 메서드
를 사용하여 필터링하였다.
- not()
선택한 요소 중 특정 선택자를 제외한 요소를 선택한다.
$(selector).not(notSelector)
그리고 클릭을 할 때 동작이 발생하기 때문에 on()
을 이용해 클릭 이벤트바인딩을 한다.
컨텐츠를 숨김/노출을 반복 할 때 가장 쉽게 생각할 수 있는 로직은 display
이다. 만약 제목을 클릭했을 때 컨텐츠가 숨겨진 상태라면 노출시키고 해당 제목에 해당하지 않는 컨텐츠는 모드 숨겨라!
하지만 탭 좀 만들어본 사람이라면 동시다발적인 숨김/노출을 제어하기 위해 선택된 순간 먼저 모든 요소에 적용된 효과를 없애줘야 한다는 것도 알기때문에 아래와 같은 코드를 작성할 수 있다.
$(function () {
$('dd:not(:first)').css('display', 'none');
$('dl dt').on('click', function () {
if ($(this).next().css('display') == 'none') {
$('dl dd').css('display', 'none');
$(this).next().css('display', 'block');
} else {
$(this).next().css('display', 'none');
}
});
});
이렇게 하면 일단 원했던 동작은 성공적으로 구현했다! 하지만 이것이 아코디언인가? 아코디언의 핵심은 아코디언을 접었다 펼 때 줄었다가 늘어나는 것에 있다.
하지만 요소를 display
로 제어하면 요소는 순간이동 하는 것 처럼 생겼다 사라졌다 하기때문에 animation
을 걸어 줄 수 없다. 학생 때 교과서 귀퉁이에 그림 그려서 만화를 만드는 것을 생각하면 된다. animaition
은 단계가 없으면 존재할 수 없다.
그래서 JQuery가 제공하는 메서드인 .slideUp
를 사용한다.
$(function () {
$('dd:not(:first)').css('display', 'none');
$('dl dt').on('click', function () {
if ($('+dd', this).css('display') == 'none') {
$('dl dd').slideUp('slow');
$('+dd', this).slideDown('slow');
}
});
});
- slideUp()
선택한 요소를 위쪽으로 서서히 사라지게 한다.
.slideUp( [duration ][, easing ] [, complete ] )
- slideDown()
선택한 요소를 아래쪽으로 서서히 나타나게 한다.
.slideUp( [duration ][, easing ] [, complete ] )
slideUp()
과 slideDown()
의 정의를 보면 요소를 마법처럼 제어하는 메서드처럼 보이지만 API를 확인해보면 요소의 높이를 조절하고 그 사이에 애니메이션을 주어 서서히 사라지는 것처럼 보여준다는 것을 알 수 있다.
위의 코드를 해석하면 아래와 같다.
dt
를 클릭했을 때,
만약this
(클릭된dt
뒤에 있는dd
)가display : none
상태라면,
모든dd
를 사라지게 하고, 해당dd
를 나타나게 한다.
즉 열려있는 컨텐츠말고 다른 컨텐츠를 클릭했을 때 항상 display : none
상태이기 때문에 컨텐츠가 모두 닫혔다가 해당 컨텐츠만 열리는 동작이 반복되는 원리로 간단한 코드만으로 아코디언 메뉴를 구현할 수 있다!
slideToggle()
선택한 요소가 현재 사라진 상태라면.slideDown()
메소드의 동작을 수행하고, 나타나 있는 상태라면.slideUp()
메소드의 동작을 수행한다.
만약 요소의 .slideDown()
.slideUp()
동작만 반복하고 싶다면 $('+dd', this).slideToggle();
처럼 요소를 선택하고 slideToggle()을 쓰는 것만으로 구현할 수 있다.
jQuery에서 제공하는 메서드를 사용하면 원하는 효과를 간단하게 구현할 수 있지만 대부분의 로직은 순수 JS로 개발할 때와 유사하기 때문에 해당 메서드가 어떤 원리로 동작하는지 API를 확인하면서 정밀하게 제어하는 방식으로 개발을 진행해야 겠다!
조금의 잡담
slideUp()
은 up이라는 단어가 주는 긍정적인 느낌때문에(ㅋㅋ) 왠지 요소를 나타나게 해줄것 같아서 조금 헷갈렸다 창문 블라인드를 생각하면 블라인드가 올라가면서 너비가 줄어드는게 당연한데!!
자매품으로 JS API 중 배열 앞에서 부터 배열값을 추가하는 unshift()
가 있다. un 때문에 왠지 부정적으로 들려서 배열값을 삭제할 것 같다 (ㅋㅋ)