본문으로 바로가기

1단 메뉴 jQuery Study #2

category Code Lab 2016. 6. 15. 13:24

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.






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