심플 세로형 2단 메뉴 - 단계별 학습 #1
이 포스팅에서는 학습 목적을 위해 생성자(클래스)함수 & 프로토타입을 이용한 세로형 2단 메뉴를 구현해 봅니다.
그리고 사용자 정의 이벤트에 대한 소스도 포함하고 있습니다.
HTML
2단 메뉴에 대한 마크업을 다음과 같이 준비합니다.
html
<ul class="acc-menu" id="accordionMenu1">
<li data-extension="close">
<div class="main-title"><span class="folder"> </span><a>menu1</a></div>
<ul class="sub">
<li><a>sub1-1</a></li>
<li><a>sub1-2</a></li>
<li><a>sub1-3</a></li>
</ul>
</li>
<li data-extension="close">
<div class="main-title"><span class="folder"> </span><a>menu2</a></div>
<ul class="sub">
<li><a>sub2-1</a></li>
<li><a>sub2-2</a></li>
</ul>
</li>
<li data-extension="close">
<div class="main-title"><span class="folder"> </span><a>menu3</a> </div>
<ul class="sub">
<li><a>sub3-1</a></li>
<li><a>sub3-2</a></li>
<li><a>sub3-3</a></li>
<li><a>sub3-4</a></li>
</ul>
</li>
<li>
<div class="main-title"><span class="folder"> </span><a>menu4</a></div>
</li>
</ul>
CSS
2단 메뉴에 대한 CSS는 다음과 같습니다.
css
#accordionMenu1 {
margin:100px;
width:300px;
}
.acc-menu {
position:relative;
list-style: none;
border:4px solid #eee;
}
.acc-menu >li {
overflow:hidden;
border:1px solid #000;
cursor:pointer;
}
.acc-menu li .main-title {
background-color:#fff;
padding:10px;
vertical-align:middle;
position:relative;
z-index:100;
}
.acc-menu li .main-title a {
display: inline-block;
height:20px;
line-height:20px;
text-align: center;
vertical-align:middle;
}
.acc-menu li .main-title .folder {
display: inline-block;
width:20px;
height:20px;
overflow:hidden;
line-height:20px;
text-align: center;
vertical-align:middle;
margin-right:5px;
text-indent: -100px;
background-image:url("https://t1.daumcdn.net/cfile/tistory/24185E335762538E2A");
/* 기본은 .close */
background-position : 0 -40px;
}
.acc-menu li .main-title .folder.empty {
background-position : 0 0;
}
.acc-menu li .main-title .folder.open {
background-position : 0 -20px;
}
.acc-menu li .main-title .folder.close {
background-position : 0 -40px;
}
.acc-menu li ul.sub {
list-style: none;
margin-left:30px;
margin-bottom:20px;
z-index:90;
position:relative;
}
.acc-menu li ul.sub.open {
display:block;
}
.acc-menu li ul.sub.hide {
display:none;
}
.acc-menu li ul.sub li {
height:20px;
padding:10px;
line-height:20px;
vertical-align:middle;
cursor:pointer;
}
.acc-menu li ul.sub li.select {
background-color: #bddeea;
}
JavaScript
javascript
$(function () {
// 인스턴스 생성
var accordion = new AccordionMenu('.acc-menu');
accordion.selectMenu(1, 1, true);
// 애니메이션 타입으로 0번째 메뉴 열기
// accordion.openSubMenuAt(0, true);
// 즉시 2번째 메뉴 닫기
// accordion.closeSubMenuAt(2, false);
accordion.$accordionMenu.on('open', function (e) {
console.log('open', e.$target.find('.main-title a').text());
});
accordion.$accordionMenu.on('close', function (e) {
console.log('close', e.$target.find('.main-title a').text());
});
accordion.$accordionMenu.on('selected', function (e) {
var oldText = '없음';
if(e.$oldItem) {
oldText = e.$oldItem.text();
}
console.log('select old = ', oldText + ', new = ' + e.$newItem.text() );
});
});
function AccordionMenu(selector) {
// 프로퍼티 생성 및 초기화
this.$accordionMenu = null; // 메뉴 랩퍼를 담을 변수
this.$mainMenuItems = null; // 메인 메뉴아이템을 담을 변수
this.$selectSubItem = null; // 선택할 서브 메뉴아이템을 담을 변수
this.init(selector);
this.initSubMenuPanel();
this._initEvent();
}
AccordionMenu.prototype = {
// 요소 초기화
'init' : function (selector) {
this.$accordionMenu = $(selector);
this.$mainMenuItems = this.$accordionMenu.children('li');
},
// 서브 패널 초기화 - 초기 시작시 닫힌 상태로 만들기
'initSubMenuPanel' : function () {
var _self = this;
this.$mainMenuItems.each(function (index) {
var $item = $(this),
$subMenu = $item.find('.sub');
// 서브 메뉴가 없는 경우
if(!$subMenu.length) {
$item.attr('data-extension', 'empty');
} else {
if ($item.attr('data-extension') == 'open') {
_self.openSbuMenu($item);
} else {
if ($item.attr('data-extension') == 'open') {
_self.openSbuMenu('$item', false);
} else {
_self.closeSubMenu($item, false);
}
}
}
});
},
// 아이콘 상태 설정
'setIconState' : function ($item, state) {
var $folder = $item.find('.main-title .folder');
// 기존 클래스를 모두 제거
$folder.removeClass();
$folder.addClass('folder ' + state);
},
// 이벤트 초기화
'_initEvent' : function () {
var _self = this,
$mainTitle = this.$mainMenuItems.find('.main-title'),
$subPanelItem = this.$mainMenuItems.find('.sub li');
$mainTitle.on('click', function (e) {
var $item = $(this).parent();
_self._toggleSubMenuPanel($item);
});
$subPanelItem.on('click', function (e) {
var $this = $(this);
_self.selectSubMenuItem($this)
});
},
// 서브 메뉴패널 열기 - animation 기본값은 true
'openSbuMenu' : function ($item, animation) {
if ($item != null) {
$item.attr('data-extension', 'open');
var $subMenu = $item.find('.sub');
if (!animation) {
$subMenu.css({
'margin-top': 0
});
} else {
$subMenu
.stop()
.animate({
'margin-top': 0
}, 300);
}
// 아이콘 상태를 open(-) 상태로 만들기
this.setIconState($item, 'open');
// 사용자 정의 open 이벤트 발생
this.dispatchEvent($item, 'open');
}
},
// 서브 메뉴패널 닫기
'closeSubMenu' : function ($item, animation) {
if ($item != null) {
$item.attr('data-extension', 'close');
var $subMenu = $item.find('.sub'),
subMenuPanelHeight = -$subMenu.outerHeight(true);
if (!animation) {
$subMenu.css({
'margin-top': subMenuPanelHeight
});
} else {
$subMenu
.stop()
.animate({
'margin-top': subMenuPanelHeight
}, 300);
}
// 아이콘 상태를 close(+) 상태로 만들기
this.setIconState($item, 'close');
// 사용자 정의 close 이벤트 발생
this.dispatchEvent($item, 'close');
}
},
// 서브 메뉴패널 열고 닫기
'_toggleSubMenuPanel' : function ($item) {
var extension = $item.attr('data-extension');
// 서브 메뉴패널이 없는 경우 실행하지 않는다.
if (extension == 'empty') {
return;
}
// 서브 메뉴 패널이 있는 경우에 실행
if (extension == 'open') {
this.closeSubMenu($item, true);
} else {
this.openSbuMenu($item, true);
}
},
// 인덱스 메뉴의 서브 메뉴패널 열기
'openSubMenuAt' : function (index, animation) {
var $item = this.$mainMenuItems.eq(index);
this.openSbuMenu($item, animation);
},
// 인덱스 메뉴의 서브 매뉴패널 닫기
'closeSubMenuAt' : function (index, animation) {
var $item = this.$mainMenuItems.eq(index);
this.closeSubMenu($item, animation);
},
// 서브 메뉴아이템 선택
'selectSubMenuItem' : function ($item) {
var $oldItem = this.$selectSubItem;
if (this.$selectSubItem != null) {
this.$selectSubItem.removeClass('select');
}
this.$selectSubItem = $item;
this.$selectSubItem.addClass('select');
// 사용자 정의 select 이벤트 발생
this._dispatchSelectEvent($oldItem, this.$selectSubItem);
},
// 인덱스를 활용한 메인 메뉴아이템과 서브 메뉴아이템 선택 기능
'selectMenu' : function (mainIndex, subIndex, animation) {
// 메인 메뉴아이템
var $item = this.$mainMenuItems.eq(mainIndex);
// 서브 메뉴아이템
var $subMenuItem = $item.find('.sub li').eq(subIndex);
// 서브 메뉴아이템이 존재하는 경우에만 처리
if($subMenuItem) {
// 서브 메뉴패널 열기
this.openSbuMenu($item, animation);
// 서브 메뉴아이템 선택
this.selectSubMenuItem($subMenuItem);
}
},
// 사용자 정의 open, close 이벤트 발생 처리
'dispatchEvent' : function ($item, eventName) {
var customEvent = jQuery.Event(eventName);
customEvent.$target = $item;
this.$accordionMenu.trigger(customEvent);
},
// 사용자 정의 select 이벤트 발생 처리
'_dispatchSelectEvent' : function ($oldItem, $newItem) {
var customEvent = jQuery.Event('selected');
customEvent.$oldItem = $oldItem;
customEvent.$newItem = $newItem;
this.$accordionMenu.trigger(customEvent);
}
};
Source code Overview
See the Pen 2단 메뉴 #1 by jaeheekim (@jaehee) on CodePen.
Related Info
- 세로형 2단 메뉴(플러그인) - 단계별 학습 #2
Jaehee's e-room
'Web Tech > jQuery' 카테고리의 다른 글
attr() 메서드와 prop() 메서드의 차이점 (0) | 2016.07.01 |
---|---|
세로형 2단 메뉴 - jQuery Study #2 (1) | 2016.06.17 |
jQuery custom event (2) | 2016.06.16 |
jQuery 유틸리티 사용자 메소드 정의 - jQuery utility method (0) | 2016.01.20 |
jQuery.expr[':'] 확장 및 활용 방법 - jQuery 가상 선택자 사용자 정의 (0) | 2016.01.19 |