이벤트 위임으로 메뉴 선택 구현하기
이벤트 델리게이션을 사용해 관리하기 편한 메뉴 구조를 만들 수 있습니다.
이벤트 델리게이션을 사용하면, 메뉴 추가나 변경에 따르는 이벤트 처리 작업을 최소화 할 수 있고, HTML/CSS 와 자바스크립트가 깨끗하게 분리되기 때문에 유지보수 및 협업에서도 유리합니다.
아래와 같은 구조를 가지는 가로 메뉴를 이벤트 델리게이션으로 메뉴 클릭 이벤트 처리를 해보겠습니다.
메뉴 항목 클릭시 AJAX로 페이지 내용을 가져와 페이지 갱신 없이 메뉴를 이동하는 웹사이트 제작에 사용할 수 있습니다.
예제의 CSS는 기본 레이아웃만 적용되어있습니다.
각 메뉴 항목은 이벤트 버블링을 이용한 이벤트 델리게이션 사용 예를 보기 위해 여러겹의 태그로 감싼 것입니다.
<style> .menuwrap{ width: 100%; text-align: center; } .menu{ position: relative; margin: 0 auto; list-style: none; } .menu li{ font-size: 0.9375em; padding: 5px 15px; text-align: center; display: inline-block; cursor: pointer; padding: 10px; } .menu li a{ padding: 10px; } .menu li a span{ padding: 10px; } .active{ font-weight: bold; border-bottom: 2px solid #a00; color: #a00; } </style>
<div class="menuwrap"> <ul class="menu"> <li class="item" data-menuid="0"><a class="active"><span>메인</span></a></li> <li class="item" data-menuid="1"><a><span>소개</span></a></li> <li class="item" data-menuid="2"><a><span>상품</span></a></li> <li class="item" data-menuid="3"><a><span>고객지원</span></a></li> <li class="item" data-menuid="4"><a><span>오시는길</span></a></li> </ul></div>
이벤트 위임(Deligation)으로 메뉴 항목 클릭에 반응하기 위해서는 중첩된 요소들 중에 어떤 것이 클릭한 건지 알아야 합니다.
개별 메뉴 항목 안에 중첩된 내부 요소에 패딩이 있으면 겹치지 않는 영역이 생기면서 <li>, <a>, <span> 중 어느 한 태그 영역을 클릭하게 됩니다.
실제로는 AJAX로 페이지 정보를 가져오기 위해 필요한 정보로 "menuid" 값이 필요하고, <li> 태그의 "menuid" 속성값을 알아야 합니다.
따라서, 중첩된 내부 요소를 클릭한 경우 상위의 <li> 요소 엘리먼트 노드를 알아내야 합니다.
콜백 함수의 이벤트 파라메터 객체에서 클릭한 타겟 요소를 얻은 후 태그 이름에 따라 부모 요소로 올라가 <li> 태그 요소를 얻습니다.
let clickli = event.target.tagName == 'A' ? event.target.parentNode: (event.target.tagName == 'SPAN' ? event.target.parentNode.parentNode : event.target);
<ll> 요소를 얻었으므로 기존 선택 메뉴의 "active" 클래스를 지우고 클릭한 새 메뉴 항목에 "active" 클래스를 추가해 활성화합니다.
마지막으로 새로 선택한 메뉴의 페이지를 가져오기 위한 "menuid" 값을 알아야 합니다.
"clickli.getAttribute('data-menuid')", 또는 "clickli.dataset.menuid" 로 "menuid" 값을 얻습니다.
이벤트 위임을 이용하면 각 메뉴 항목마다 이벤트를 등록할 필요가 없어집니다.없이,
전체 메뉴 항목 영역에 이벤트 리스너를 등록함으로써 이벤트 버블링(Event Bubbling)으로 올라오는 이벤트를 캡쳐해 선택한 메뉴에 대한 정보를 얻을 수 있습니다.
document.addEventListener('DOMContentLoaded', function(){ // DOM 로딩 후 이벤트 리스너 등록 document.querySelector('.menu').addEventListener('click', function(e){ // 전체 메뉴영역을 클릭하는 이벤트 리스너 등록 //클릭 메뉴 항목 찾기 let target = e.target; // 클릭한 엘리먼트 //UL 리스트 아이템인 <li> 엘리먼트를 얻기 위해 <li> 하위에서 클릭한 태그에 따라 상위 부모를 찾는 방법을 다르게함. let clickli = target.tagName == 'A' ? target.parentNode: (target.tagName == 'SPAN' ? target.parentNode.parentNode : target); if(clickli){ //현재 활성 메뉴 초기화 let currentCategory = document.querySelector('.menu a.active'); if(currentCategory){ currentCategory.classList.remove('active'); } //새 선택 메뉴 활성화 clickli.querySelector('a').classList.add('active');
let pageURL = "/api/page?menuid="+clickli.getAttribute('data-menuid'); //AJAX로 새로 로딩할 새 페이지 내용을 가져옴. } });});