1단 메뉴 제이쿼리 스터디 단계별 학습 #2
이 글에서는 1단 메뉴 단계별 학습 #1의 코드를 가지고 jQuery 플러그인으로 만드는 방법에 대해 알아봅니다.
플러그인은 어디에나 꽂을 수 있도록 만드는 목적이 있기에 여기서는 동일한 1단 메뉴를 2개 만들어 테스트해보도록 합니다.
HTML
2개의 동일한 1단 메뉴에 대한 마크업은 다음과 같습니다.
html
<div class="bar-menu" id="barMenu1">
<ul class="menu-body">
<li>MENU 01</li>
<li>MENU 02</li>
<li>MENU 03</li>
<li>MENU 04</li>
<li>MENU 05</li>
</ul>
<div class="bar"> </div>
</div>
<div class="bar-menu" id="barMenu2">
<ul class="menu-body">
<li>First MENU</li>
<li>Second MENU</li>
<li>third MENU</li>
<li>forth MENU 04</li>
<li>fifth MENU 05</li>
</ul>
<div class="bar"> </div>
</div>
CSS
css
* { padding:0; margin:0; }
body{ font-size:9pt; }
#barMenu1 { position:relative; left:100px; top:100px; width:450px; }
#barMenu2 { left: 100px; top: 150px;}
.bar-menu { position:relative; height:50px; cursor:pointer; overflow: hidden; }
.bar-menu ul.menu-body { position:relative; list-style: none; height:100%; }
.bar-menu ul.menu-body li {
height:20px; padding:10px; margin-left:10px; float:left;
line-height:20px; vertical-align:middle; border:2px solid #93b0bc;
display:inline-block; border-radius: 10px;
}
/* 선택 메뉴 아이템 스타일 */
.bar-menu ul.menu-body li.select { background-color: #bddeea; }
/* 오버 메뉴 아이템 스타일 */
.bar-menu ul.menu-body li.over { background-color:#eee; }
.bar-menu .bar { width:0; height:5px; background-color: #468ebc; position: absolute; left:0; bottom:0; }
JavaScript
jQuery 플러그인을 만드는 문법을 간략히 소개한다면 일반적으로 다음과 같습니다.
javascript
(function ($) {
$.fn.플러그인이름 = function () {
this.each(function () {
// 기능 구현
});
return this;
}
})(jQuery);
위의 플러그인 문법에 적합하도록 기존에 작성한 메뉴 스크립트를 다음과 같이 리팩토링할 수 있습니다.
javascript
// 다른 라이브러리와의 충돌을 피하기 위해 즉시실행 함수에 플러그인을 정의한다.
(function ($) {
// barMenu 플러그인 정의
$.fn.barMenu = function () {
this.each(function (index) {
var barMenu = new BarMenu(this);
// setSelectMenuItemAt() 메서드를 사용하기 위해
// barMenu() 플러그인 내부에서 생성한 barMenu 객체를
// jQuery 객체에 저장해 둔다.
$(this).data('barMenu', barMenu);
});
return this;
};
// 외부에서 n번째 인덱스에 해당하는 메뉴아이템을 선택할 수 있는 기능을 플러그인으로 정의
// barMenu 미리 선택 처리하기 위한 플러그인
$.fn.selectBarMenuAt = function (selectedIndex, animation) {
this.each(function (index) {
var barMenu = $(this).data('barMenu');
if(barMenu) {
barMenu.setSelectMenuItemAt(selectedIndex, animation);
}
});
return this;
};
}(jQuery));
// DOM 이 준비되면 호출
$(function () {
// 사용자 정의 이벤트
// barMenu 플러그인을 사용해 사용자 정의한 select 이벤트 리스너 등록하기
var $barMenu = $('.bar-menu').barMenu();
$barMenu.on('selected', function (e) {
var oldIndex = -1;
if (e.$oldItem) {
oldIndex = e.$oldItem.index();
}
console.log('old = ' + oldIndex + ', new = ' + e.$newItem.index());
});
// #barMenu1의 1번째 메뉴 아이템 선택 처리
$barMenu.eq(0).selectBarMenuAt(1, false);
// #barMenu1의 4번째 메뉴 아이템 선택 처리
$barMenu.eq(1).selectBarMenuAt(4, false);
});
/**
* --------------------------
* 생성자(클래스) 정의
* --------------------------
**/
function BarMenu(selector) {
// 프로퍼티 생성
this.$barMenu = null;
this._$menuBody = null;
this._$menuItems = null;
this._$overItem = null;
this._$bar = null;
// 선택 메뉴 아이템
this.$selectItem = null;
this._init(selector);
this._initEvent();
}
BarMenu.prototype = {
// 요소 초기화
"_init" : function (selector) {
this.$barMenu = $(selector);
this._$menuBody = this.$barMenu.find('.menu-body');
this._$menuItems = this._$menuBody.find('li');
this._$bar = this.$barMenu.find('.bar');
},
// 이벤트 초기화
"_initEvent" : function () {
var _self = this;
// 오버 메뉴 및 선택(클릭한)메뉴 이벤트 호출
this._$menuItems.on('mouseenter click', function (e) {
var $this = $(this);
if(e.type == 'mouseenter') {
_self._setOverMenuItem($this);
} else if(e.type == 'click') {
_self.setSelectMenuItem($this);
}
});
// 전체 메뉴 영역을 벗어난 경우
this.$barMenu.on('mouseleave', function () {
var $this = $(this);
// 기존 오버 메뉴아이템이 있는 경우 제거
_self._removeOverItem($this);
// 기존 선택한 메뉴아이템이 있는 경우 선택처리
_self._reSelectMenuItem();
});
},
// 오버 메뉴아이템 처리하기
"_setOverMenuItem" : function ($item) {
// 기존 오버되어 있는 아이템이 있다면 over 스타일을 제거
if(this._$overItem) {
this._$overItem.removeClass('over');
}
// 신규 오버 메뉴아이템이 선택한 메뉴아이템과 같지 않은 경우에만 메뉴아이템 스타일 적용하기
// 선택 메뉴 아이템 인덱스 값 구하기
var selectIndex = -1;
if (this.$selectItem != null) {
selectIndex = this.$selectItem.index();
}
// 신규 오버 메뉴아이템 인덱스 값과 선택한 메뉴아이템의 인덱스 값을 비교
if ($item.index() != selectIndex) {
this._$overItem = $item;
this._$overItem.addClass('over');
} else {
this._$overItem = null;
}
// 메뉴바 이동
this._moveBar($item);
},
// 전체 메뉴 영역 벗어날 때 오버 효과 제거
"_removeOverItem" : function () {
if(this._$overItem) {
this._$overItem.removeClass('over');
}
this._$overItem = null;
this._moveBar(null);
},
// 이동 메뉴 효과
"_moveBar" : function ($item, animation) {
var left = -100,
width = 0;
if ($item != null) {
left = $item.position(true).left + parseInt($item.css('margin-left'));
width = $item.outerWidth();
}
if(animation == false) {
// 애니메이션 없이 바로 이동
this._$bar.css({
'left': left,
'width' : width
})
} else {
// 애니메이션 이동
this._$bar
.stop()
.animate({
'left': left,
'width': width
}, 300)
}
},
// 선택(클릭) 메뉴 아이템 처리
'setSelectMenuItem' : function ($item, aniamtion) {
var $oldItem = this.$selectItem;
// 기존에 선택한 메뉴가 있는 경우 처리
if(this.$selectItem) {
this.$selectItem.removeClass('select');
}
this.$selectItem = $item;
this.$selectItem.addClass('select');
// 메뉴바 이동
this._moveBar($item, aniamtion);
// 사용자 정의 이벤트 발생(호출)
this._dispatchSelectEvent($oldItem, $item);
},
// 기존 선택한 메뉴아이템이 있는 경우 선택 처리
"_reSelectMenuItem" : function () {
if(this.$selectItem) {
this._moveBar(this.$selectItem);
}
},
// 메뉴아이템이 선택된 상태에서 시작하도록 설정하는 경우
'setSelectMenuItemAt' : function (index, animation) {
var target = this._$menuItems.eq(index);
this.setSelectMenuItem(target, animation);
},
// 사용자 정의 이벤트 발생
"_dispatchSelectEvent" : function ($oldItem, $newItem) {
var customEvent = jQuery.Event('selected');
customEvent.$oldItem = $oldItem;
customEvent.$newItem = $newItem;
this.$barMenu.trigger(customEvent);
}
};
Result View
See the Pen 1단 메뉴 #2 by jaeheekim (@jaehee) on CodePen.
Related Info
- 1단 메뉴(프로토타입) - 단계별 학습 #1
Jaehee's WebClub
'Code Lab' 카테고리의 다른 글
반응형 탭 메뉴 (0) | 2016.06.20 |
---|---|
메뉴 슬라이딩 효과 (0) | 2016.06.17 |
1단 메뉴 jQuery Study #1 (0) | 2016.06.15 |
simple tab menu (0) | 2016.06.13 |
jQuery 사용자 유틸리티 - 세자릿수 콤마 찍어보기 (0) | 2016.06.02 |