작업을 하면서 가장 많이 구현하게 되는 기능 중 하나인 탭메뉴를 바닐라 스크립트를 사용하여 구현하려한다.
구현은 크게 2가지 방법으로 할 것이다.
1. 탭 버튼과 탭 컨텐츠가 한 영역으로 묶여 있는 경우
<style>
.tab_menu{position:relative;}
.tab_menu .list{overflow:hidden;}
.tab_menu .list li{float:left;}
.tab_menu .list .btn{font-size:13px; margin-right:14px;}
.tab_menu .list .cont{display:none; position:absolute; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
.tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
.tab_menu .list li.is_on .cont{display:block;}
</style>
<div class="tab_menu">
<ul class="list">
<li class="is_on">
<a href="#tab1" class="btn">Tab Button1</a>
<div id="tab1" class="cont">Tab Content1</div>
</li>
<li>
<a href="#tab2" class="btn">Tab Button2</a>
<div id="tab2" class="cont">Tab Content2</div>
</li>
<li>
<a href="#tab3" class="btn">Tab Button3</a>
<div id="tab3" class="cont">Tab Content3</div>
</li>
</ul>
</div>
위와 같이 .btn과 .cont가 하나의 li 태그로 묶여서 li태그를 제어하면
버튼과 컨텐츠 영역을 모두 제어할 수 있게 마크업을 하는 방법에선
특별하게 많은 스크립트가 필요하지 않다.
이 방법은 스크립트를 간소화 할 수 있는 장점과 동시에
마크업 구조도 접근성을 높일 수 있는 구조로 작성을 했기 때문에 좀 더 괜찮은 소스라 생각한다.
(!)
키보드 접근성을 고려하면 위의 소스처럼 버튼 영역 다음에 바로 해당 버튼과 연관 된 컨텐츠가 잡히기 때문에
접근성 측면에서 위와 같은 구조로 탭 메뉴를 작성하는 것을 추천한다.
<style>
.tab_menu{position:relative;}
.tab_menu .list{overflow:hidden;}
.tab_menu .list li{float:left; margin-right:14px;}
.tab_menu .list .btn{font-size:13px;}
.tab_menu .list .cont{display:none; position:absolute; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
.tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
.tab_menu .list li.is_on .cont{display:block;}
</style>
<div class="tab_menu">
<ul class="list">
<li class="is_on">
<a href="#tab1" class="btn">Tab Button1</a>
<div id="tab1" class="cont">Tab Content1</div>
</li>
<li>
<a href="#tab2" class="btn">Tab Button2</a>
<div id="tab2" class="cont">Tab Content2</div>
</li>
<li>
<a href="#tab3" class="btn">Tab Button3</a>
<div id="tab3" class="cont">Tab Content3</div>
</li>
</ul>
</div>
<script>
const tabList = document.querySelectorAll('.tab_menu .list li');
for(var i = 0; i < tabList.length; i++){
tabList[i].querySelector('.btn').addEventListener('click', function(e){
e.preventDefault();
for(var j = 0; j < tabList.length; j++){
tabList[j].classList.remove('is_on');
}
this.parentNode.classList.add('is_on');
});
}
</script>
위와 같이 스크립트를 작성하면 간단하게 탭을 구현할 수 있다.
1. .tab_menu .list의 li안에 있는 각 버튼에 addEventListener로 'click' 이벤트를 걸어준다. (for문을 통해)
2. 해당 이벤트에 preventDefault()로 a태그 클릭시 href로 인한 화면 이동을 막아준다.
3. 버튼을 클릭시 해당 버튼의 부모요소(li태그)에 is_on 클래스를 준다.
4. 3번 단계 전에 버튼 클릭했을 때 다른 부모요소(다른 li태그들)의 is_on 클래스를 해제시켜준다.
* 기존에 css로 .is_on 클래스에 의해 컨텐츠의 display가 none/block 되도록 설정해놓았기 때문에 is_on 클래스로 탭을 구현한다.
*버튼 (a태그)마다 href를 통해 해당 탭에 해당하는 컨텐츠의 id값과 연결시켜준 이유는
해당 버튼과 컨텐츠의 접근성을 위해 연결해놓은 것이다. (버튼과 컨텐츠를 연결)
2. 탭 버튼과 탭 컨텐츠가 다른 영역에 위치하고 있을 때
<style>
.tab_menu{position:relative;}
.tab_menu .list{overflow:hidden;}
.tab_menu .list li{float:left; margin-right:14px;}
.tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
.tab_menu .list .btn{font-size:13px;}
.tab_menu .cont_area .cont{position:absolute; top:25px; left:0; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
</style>
<div class="tab_menu">
<ul class="list">
<li class="is_on">
<a href="#tab1" class="btn">Tab Button1</a>
</li>
<li>
<a href="#tab2" class="btn">Tab Button2</a>
</li>
<li>
<a href="#tab3" class="btn">Tab Button3</a>
</li>
</ul>
<div class="cont_area">
<div id="tab1" class="cont">
Tab Content1
</div>
<div id="tab2" class="cont">
Tab Content2
</div>
<div id="tab3" class="cont">
Tab Content3
</div>
</div>
</div>
작업을 하다보면 1번에서 소개한 바와 같이 버튼과 컨텐츠를 한 군데 묶기 힘든 경우도 많이 생긴다.
위의 소스와 같이 탭과 컨텐츠가 마크업상 분리 되어있을 때 사용할 수 있는 탭 구현 방법을 소개한다.
여기서 중요한 점은 1번과 마찬가지로 버튼(a태그)의 href 속성과
각 탭에 해당하는 컨텐츠 영역의 id값을 일치 시켜서 접근성을 높여주는 것이다.
<style>
.tab_menu{position:relative;}
.tab_menu .list{overflow:hidden;}
.tab_menu .list li{float:left; margin-right:14px;}
.tab_menu .list li.is_on .btn{font-weight:bold; color:green;}
.tab_menu .list .btn{font-size:13px;}
.tab_menu .cont_area{margin-top:10px;}
.tab_menu .cont_area .cont{display:none; background:#555; color:#fff; text-align:center; width:250px; height:100px; line-height:100px;}
</style>
<div class="tab_menu">
<ul class="list">
<li class="is_on">
<a href="#tab1" class="btn">Tab Button1</a>
</li>
<li>
<a href="#tab2" class="btn">Tab Button2</a>
</li>
<li>
<a href="#tab3" class="btn">Tab Button3</a>
</li>
</ul>
<div class="cont_area">
<div id="tab1" class="cont">
Tab Content1
</div>
<div id="tab2" class="cont">
Tab Content2
</div>
<div id="tab3" class="cont">
Tab Content3
</div>
</div>
</div>
<script>
const tabList = document.querySelectorAll('.tab_menu .list li');
const contents = document.querySelectorAll('.tab_menu .cont_area .cont')
let activeCont = ''; // 현재 활성화 된 컨텐츠 (기본:#tab1 활성화)
for(var i = 0; i < tabList.length; i++){
tabList[i].querySelector('.btn').addEventListener('click', function(e){
e.preventDefault();
for(var j = 0; j < tabList.length; j++){
// 나머지 버튼 클래스 제거
tabList[j].classList.remove('is_on');
// 나머지 컨텐츠 display:none 처리
contents[j].style.display = 'none';
}
// 버튼 관련 이벤트
this.parentNode.classList.add('is_on');
// 버튼 클릭시 컨텐츠 전환
activeCont = this.getAttribute('href');
document.querySelector(activeCont).style.display = 'block';
});
}
</script>
위와 같이 작성할 경우 아래의 화면처럼
동일한 탭 효과를 구현할 수 있다.
1번과 다르게 탭 버튼과 탭 컨텐츠가 따로 떨어져 있더라도
위의 소스의 activeCont 처럼 현재 활성화 할 탭 컨텐츠를 변수로 받아서
탭 버튼을 클릭할 때마다 a태그의 href 값을 받아와 해당하는 id값을 가진 컨텐츠를 보여준다.
'Frontend > Javascript' 카테고리의 다른 글
Javascript (자바스크립트) 활용 - 스크롤시 고정되는 영역 만들기 (0) | 2021.06.24 |
---|---|
Javascript (자바스크립트) - 높이, 너비 구하기 (offsetHeight, clientHeight, scrollHeight, offsetWidth...) (0) | 2021.04.25 |
Javascript (자바스크립트) - 요소 찾기 (querySelector, querySelectorAll) (0) | 2020.10.12 |
Javascript (자바스크립트) - 변수 선언 (var, let, const) (0) | 2020.06.03 |
Javascript (자바스크립트) - 요소(Element) 찾기 (0) | 2020.03.29 |