제이쿼리 사용자 정의 이벤트 질문입니다.

제이쿼리 사용자 정의 이벤트 질문입니다.

QA

제이쿼리 사용자 정의 이벤트 질문입니다.

본문

tabMenu1.$tabMenu.on("tabSelect", function(e) {
                tabPanel1.setSelectPanel(e.selectIndex);
            })
 
 
tabMenu1.$tabMenu은
tabMenu1 객체의 키값으로 $tabMenu가 소속되어있습니다.
tabMenu1 or $tabMenu 둘중 하나를 지우고 실행하면 실행안됩니다.
var tabMenu1 = tabMenu("#tabMenu1"); 여기서 tabMenu1라는 ID값을 변수에 넣어 초기화했는데요 왜 tabMenu("#tabMenu1")라는 값이 붙어야만 실행이 되는거죠?
 
$tabMenu는 사용자 정의 이벤트를 만들기 위해서 dispatchSelectEvent()안에 있는$tabmenu.trigger(event)와 함께 꼭 써야하구요
 
        var tabPanel1 = null;
        $(document).ready(function() {
            // 탭메뉴 코드가 동작할 수 있도록 tabMenu() 함수 호출
            var tabMenu = $("#tabMenu1");
            $tabMenu.on("tabSelect", function(e) {
                tabPanel1.setSelectPanel(e.selectIndex);
            })
            // 탭패널 기능 호출
아래쪽 전체소스코드에서 유사한 부분을 위와같이 수정해도 실행이 안됩니다.
 
문제점이 뭔지 모르겠습니다. 답변 부탁드립니다.
 

<!DOCTYPE HTML> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<title> </title> 
<style> 
.tab-menu { 
     list-style: none; 
     height:80px; 
} 
.tab-menu li { 
    width:99px; 
    height:40px; 
    background-position-y:0; 
    text-indent: -1000px; 
    overflow: hidden; 
    display: inline-block; 
    float:left; 
} 
.tab-menu li:hover { 
     background-position-y: -40px; 
} 
.tab-menu li.select { 
     background-position-y: -80px; 
     height:80px; 
} 
.tab-menu li.menuitem1 { 
     background-image: url(./images/newbtn.bar.1.png);  
} 
.tab-menu li.menuitem2 { 
     background-image: url(./images/newbtn.bar.2.png);  
} 
.tab-menu li.menuitem3 { 
     background-image: url(./images/newbtn.bar.3.png);  
} 
.tab-menu li.menuitem4 { 
     background-image: url(./images/newbtn.bar.4.png);  
} 
.tab-menu li.menuitem5 { 
     background-image: url(./images/newbtn.bar.6.png);  
} 
.tab-contents { 
    position:relative; 
    left:10px; 
    top:10px; 
    width:780px; 
    height:340px; 
    overflow:hidden; 
    background:url(./images/content_bg.png) no-repeat; 
} 
.tab-contents .content{ 
    position: absolute; 
    left:10px; 
    top:10px; 
    display:none; 
} 
.tab-contents .content.select{ 
    display:block; 
} 
</style> 
<script src="../../../libs/jquery-1.11.0.min.js"></script> 
<script> 
var tabPanel1 = null; 
$(document).ready(function() { 
    // 탭메뉴 코드가 동작할 수 있도록 tabMenu() 함수 호출 
    var tabMenu1 = tabMenu("#tabMenu1"); 
    tabMenu1.$tabMenu.on("tabSelect", function(e) { 
        tabPanel1.setSelectPanel(e.selectIndex); 
  }) 
  // 탭패널 기능 호출 
  tabPanel1 = tabPanel(".tab-contents"); 
}); 
// 탭메뉴 기능 구현하기 
function tabMenu(selector) { 
    var $tabMenu = null; 
    var $menuItems = null; 
    // 선택 한 탭메뉴를 저장할 변수 
    var $selectMenuItem = null; 
    // 요소 초기화, tabMenu() 함수 내부에서 사용할 공통 데이터는 모두 이곳에 작성해주세요. 
    function init() { 
        $tabMenu = $(selector); 
        $menuItems = $tabMenu.find("li"); 
  } 
  // 이벤트 등록은 모두 이곳에 작성해주세요. 
  function initEvent() { 
      $menuItems.click(function() { 
          setSelectItem($(this)); 
    }); 
} 
// 선택 메뉴 아이템 만들기 
function setSelectItem($item) { 
    if ($selectMenuItem) { 
        $selectMenuItem.removeClass("select"); 
  } 
  $selectMenuItem = $item; 
  $selectMenuItem.addClass("select"); 
  // 이벤트 발생 
  dispatchSelectEvent(); 
} 
// index번째 메뉴 아이템 선택 
function setSelectItemAt(index) { 
    var $item = $menuItems.eq(index); 
    setSelectItem($item); 
} 
// 이벤트 발생 
function dispatchSelectEvent() { 
    // 이벤트 객체 생성 
    var event = jQuery.Event("tabSelect"); 
    // 이벤트에 담아 보낼 데이터 연결 
    event.selectIndex = $selectMenuItem.index(); 
    event.$selectItem = $selectMenuItem; 
    $tabMenu.trigger(event); 
} 
init(); 
initEvent(); 
return { 
    $tabMenu : $tabMenu, 
    setSelectItemAt : setSelectItemAt 
} 
} 
// 탭패널 기능 구현하기 
function tabPanel(selector) { 
    var $tabPanels = null; 
    var $selectPanel = null; 
    function init(selector) { 
        $tabPanels = $(selector).find(".content"); 
  } 
  function setSelectPanel(index) { 
      if ($selectPanel) { 
          $selectPanel.removeClass("select"); 
    } 
    $selectPanel = $tabPanels.eq(index); 
    $selectPanel.addClass("select"); 
} 
init(selector); 
// 선택기능 리턴 
return { 
    setSelectPanel : setSelectPanel 
} 
} 
</script> 
</head> 
<body> 
<ul class="tab-menu" id="tabMenu1"> 
<li class="menuitem1">google</li> 
<li class="menuitem2">facebook</li> 
<li class="menuitem3">pinterest</li> 
<li class="menuitem4">twitter</li> 
<li class="menuitem5">path</li> 
</ul> 
   
<div class="tab-contents"> 
<div class="content"> 
<img src="images/content_facebook.png"> 
</div> 
<div class="content"> 
<img src="images/content_google.png"> 
</div>     
<div class="content"> 
<img src="images/content_pinterest.png"> 
</div> 
<div class="content"> 
<img src="images/content_twitter.png"> 
</div> 
<div class="content"> 
<img src="images/content_path.png"> 
</div> 
</div> 
</body> 
</html>

이 질문에 댓글 쓰기 :

답변 6

일단, 코드는 동작을 합니다.

 


// 1.
// tabMenu 함수에서 tab 클릭하면
// 메뉴에 select class 를 추가하고
// select 된 index 와 element 를 담은 이벤트를 trigger 합니다.
// 이때 이 tabMenu 함수는 selector 를 인자로 받죠. html 에서 뭐가 메뉴인지 알아야 하니까..
function tabMenu(selector) {
   ....
}

2. tabMenu 와 tabPanel 을 연결시켜주는 코드는..


        $(document).ready(function () {
            // 탭메뉴 코드가 동작할 수 있도록 tabMenu() 함수 호출
            var tabMenu1 = tabMenu("#tabMenu1");
            // 탭패널 기능 호출
            var tabPanel1 = tabPanel(".tab-contents");
            tabMenu1.$tabMenu.on("tabSelect", function (e) {
                tabPanel1.setSelectPanel(e.selectIndex);
            });
        });

맞게 하신것 같네요...

1. $.extend(obj1, obj2, obj3); 이렇게 있으면.. obj3 를 obj2 에 합치고, 그 결과를 다시 obj1 에 합치는 것이니 말씀하신게 맞습니다.
2. 그 부분은 제 실수네요 ^^;;;

...
// jQuery chainning 을 위해 반환
return this.each(function() {
// $(this) 는 $(메뉴) 에 해당함
$(this).each(function (idx, ele) {
...
// 이것은 그냥.. 이렇게 되야겠네요. this == $('.tabMenu') 입니다.
return this.each(function(idx, ele) {
   ...
};
3. tabMenu1.$tabMenu.on("tabSelect", function(e) {} .. 이건 체이닝이라기 보단.. tabMenu1 의 attribute 에 $tabMenu 가 있는 것입니다.
   말씀하신대로

tabMenu1 = {
    $tabMenu : $tabMenu,
    setSelectItemAt : setSelectItemAt
};

   이기 때문에, tabMenu1.$tabMenu.on() .. 이렇게 호출할 수 있는 것이 맞습니다.
   따라서 이경우에는 chaining 이 아닙니다. chaining 은 메소드가 자기 자신을 다시 반환함으로써 이어서 다른 함수를 계속 호출할 수 있도록 하는 것입니다.
   $(selector).myTab().setBorder() 처럼 myTab() 안에서 object 자신인 $(selector) 를 반환함으로써 또 다시 setBorder() 를 호출할 수 있도록 하는 것입니다.
   마찬가지로 setBorder()에서도 $(selector) 자신을 반환해서 다른 플러그인을 연속해서 호출 할 수 있도록 하는 것입니다.

약간 소스를 고쳐보면요..

- ul 태그에 탭 패널들을 감싸는 wrapper 의 ID 를 지정 (data-panel-wrapper="tabContent1")

- 각 탭 메뉴들에 연결되는 탭 패널들의 ID 를 지정 (data-panel="pan1" --> 클릭시 '#pan1' 을 선택)

- MyTab 함수에서 selector 를 클래스로 받게되면, 한페이지에 탭이 여러개 있어도 한번에 처리 가능

 


<ul class="tabMenu" data-panel-wrapper="tabContent1">
    <li data-panel="pan1">탭 메뉴1</li>
    <li data-panel="pan2">탭 메뉴2</li>
    <li data-panel="pan3">탭 메뉴3</li>
    <li data-panel="pan4">탭 메뉴4</li>
    <li data-panel="pan5">탭 메뉴5</li>
</ul>
<div id="tabContent1">
    <div id="pan1" class="tab-panel">탭 컨텐츠 1</div>
    <div id="pan2" class="tab-panel">탭 컨텐츠 2</div>
    <div id="pan3" class="tab-panel">탭 컨텐츠 3</div>
    <div id="pan4" class="tab-panel">탭 컨텐츠 4</div>
    <div id="pan5" class="tab-panel">탭 컨텐츠 5</div>
</div>
<script>
        $(document).ready(function () {
            MyTab('.tabMenu');
        });
        // 탭메뉴 기능 구현하기
        function MyTab(selector) {
            $(selector).each(function(idx, ele) {
                // 탭 메뉴 레퍼 (UL)
                let $tabMenu = $(ele);
                // 각 메뉴들 (LI 태그)
                let $menuItems = $tabMenu.find("li");
                // 탭 패널들 (UL 태그의 'data-panel-wrapper' 속성을 읽어와 그 안의 '.tab-panel' 클래스들을 찾음)
                let $tabPanels = $('#'+$tabMenu.data('panel-wrapper')).find('.tab-panel');
                // 메뉴 클릭 이벤트 핸들러
                function setSelectItem($item) {
                    $menuItems.removeClass('select');
                    $item.addClass('select');
                    $tabPanels.removeClass('select');
                    $tabPanels.eq($item.index()).addClass('select');
                }
                // 클릭 이벤트 등록
                $menuItems.click(function () {
                    setSelectItem($(this));
                });
            });
        }
</script>

첫번째 질문 부터

=======================

var tabMenu1 = tabMenu("#tabMenu1"); 여기서 tabMenu1라는 ID값을 변수에 넣어 초기화했는데요 왜 tabMenu("#tabMenu1")라는 값이 붙어야만 실행이 되는거죠?

===================

var 뒤에 붙은 tabMenu1은 그냥 변수입니다.  그냥 xx1이라고 바꾸어도 됩니다. (67번째 줄도)

#tabMenu1은 114번째 줄의 tabMenu1입니다.  이것을 둘다 yy2로 바꾸셔도 됩니다.

 

그리고 tabMenu는 함수명의 tabMenu이고..(66째줄)

67번째줄의 $tabMenu는 75번째줄의 변수입니다. zz3로 바꾸면

id=yy2를 tabMenu라는 함수로 처리한후에 xx1변수에 담아서 xx1안에 있는 변수 zz3를 선택하고 그것의 on event를 선택하는 것 같습니다. 

JQeury가 어렵네요. ㅠㅠ

 

 

사용자 이벤트 처리를 할 수 있도록.. jQuery 플러그인 형태로 만들면..


<ul class="tabMenu" data-panel-wrapper="tabContent1">
    <li data-panel="pan1">탭 메뉴1</li>
    <li data-panel="pan2">탭 메뉴2</li>
    <li data-panel="pan3">탭 메뉴3</li>
    <li data-panel="pan4">탭 메뉴4</li>
    <li data-panel="pan5">탭 메뉴5</li>
</ul>
<div id="tabContent1">
    <div id="pan1" class="tab-panel">탭 컨텐츠 1</div>
    <div id="pan2" class="tab-panel">탭 컨텐츠 2</div>
    <div id="pan3" class="tab-panel">탭 컨텐츠 3</div>
    <div id="pan4" class="tab-panel">탭 컨텐츠 4</div>
    <div id="pan5" class="tab-panel">탭 컨텐츠 5</div>
</div>
<script>
(function($) {
    // JQuery 플러그인에 등록
    // - options: 사용자 지정 옵션
    $.fn.myTab = function(options) {
        // 옵션을 기본 설정과 병합
        let settings = $.extend({
            onBeforeTabChange : null,
            onAfterTabChange : null,
        }, options);
        // 지정된 selector 에 해당하는 모든 elements 들을 순회
        // jQuery chainning 을 위해 반환
        return this.each(function() {
            // $(this) 는 $(메뉴) 에 해당함
            $(this).each(function (idx, ele) {
                // 탭 메뉴 레퍼 (UL)
                let $tabMenu = $(ele);
                // 각 메뉴들 (LI 태그)
                let $menuItems = $tabMenu.find("li");
                // 탭 패널들 (UL 태그의 'data-panel-wrapper' 속성을 읽어와 그 안의 '.tab-panel' 클래스들을 찾음)
                let $tabPanels = $('#' + $tabMenu.data('panel-wrapper')).find('.tab-panel');
                // 메뉴 클릭 이벤트 핸들러
                function setSelectItem($item) {
                    // onBeforeTabChange 가 옵션으로 설정되었다면 호출
                    if (settings.onBeforeTabChange) {
                        settings.onBeforeTabChange($item);
                    }
                    $menuItems.removeClass('select');
                    $item.addClass('select');
                    $tabPanels.removeClass('select');
                    let $clickedPanel = $tabPanels.eq($item.index());
                    $clickedPanel.addClass('select');
                    // onAfterTabChange 가 옵션으로 설정되었다면 호출
                    if (settings.onAfterTabChange) {
                        settings.onAfterTabChange($item, $clickedPanel);
                    }
                }
                // 클릭 이벤트 등록
                $menuItems.click(function () {
                    setSelectItem($(this));
                });
            });
        });
    };
})(jQuery);
$(document).ready(function () {
    $('.tabMenu').myTab({
        // 탭이 클릭된 후 메뉴가 변경되기 바로 직전 호출
        // - $menu : 클릭된 메뉴
        onBeforeTabChange: function($menu) {
            console.log('before tab change : ', $menu);
        },
        // 탭이 클릭된 후 메뉴가 변경된 바로 직후 호출
        // - $menu : 클릭된 메뉴
        // - $panel : 선택된 탭 패널
        onAfterTabChange: function($menu, $panel) {
            console.log('after tab change : ', $menu, $panel);
        }
    });
});
</script>

 

이런식으로 됩니다. 공부하시는데 도움이 되시라고 만들어봤습니다.

답변 감사드립니다.
적어주신 소스 실행해보았는데요 어렵습니다. 초보입니다.
  먼저 $.fn.myTab 라면 fn는 function 약자인가요? jQuery에서 공식으로 지원하는 키워드인지 궁금합니다.

$.fn.doThing = function() { };    //  jQuery.prototype 에 함수 추가라는데요 myTab이면 jquery.prototype인가요?

그리고 제가 아직 책에서 extend 메서드를 못 마주쳤는데요
let settings = $.extend({
settings 변수는 $.extend 블록 내에서만 실행되는거구요
onbefore,onaftertabchange 변수 값에 null이 할당됩니다.

그리고
// onBeforeTabChange 가 옵션으로 설정되었다면 아래 if문이 호출되는 건데요
if (settings.onBeforeTabChange) {

아래 onbeforeTabChange가 호출된 이 후에 위에 if문이 호출되는겁니다.
$(document).ready(function () {
    $('.tabMenu').myTab({
        // 탭이 클릭된 후 메뉴가 변경되기 바로 직전 호출
        // - $menu : 클릭된 메뉴
        onBeforeTabChange: function($menu) {
            console.log('before tab change : ', $menu);

전체적으로 저에게 난이도가 상이여서 이해가 안됩니다....

끝으로
// $(this) 는 $(메뉴) 에 해당함
29
            $(this).each(function (idx, ele) {
$(this)는 $(메뉴)에 해당한다고 하셨는데 어느부분에서 this가 메뉴를 참조하는지 궁금합니다.

쉽게 설명 부탁드립니다.

1. $.fn.PLUGIN_NAME = function() {}; jQuery 플러그인 등록하는 방법입니다.

2. $.extend 는 object 들을 merge 하는 함수입니다. https://api.jquery.com/jquery.extend/

3. "아래 onbeforeTabChange가 호출된 이 후에 위에 if문이 호출되는겁니다."

   그 아래에 있는 `onbeforeTabChange` 는 myTab 에 파라미터로 전달되는 인자입니다. 거기서 실행되는게 아니고. extend 함수에 의해서 settings 에 합쳐져서, 플러그인 내에서 호출이 되는거죠. 즉, 이벤트 처리루틴을 함수에 인자로 넘겨주는 것입니다.

 

간단한 코드로 설명드릴테니 아래를 천천히 읽어보세요.

 

 

아래와 같은 HTML 코드가 있다고 하고 코드로 설명을 드려봅니다.


<div id="article">
    <h1>제목</h1>
    <p>내용</p>
</div>
<ul>
    <li class="lst">목록1</li>
    <li class="lst">목록2</li>
    <li class="lst">목록3</li>
</ul>

기본적인 jQuery 플러그인을 작성해보겠습니다. $(selector).printOut() 이라고 호출하면 selector 에 해당하는 HTML element 의 내용을 콘솔에 출력하는 것입니다.


<script>
(function($) {
    // `printOut` 이라는 jQuery 플러그인 등록
    $.fn.printOut = function() {
        // HTML element 의 내용을 콘솔에 출력
        console.log(this.html());
    };
})(jQuery);
$(document).ready(function () {
    // `#article` 의 내용을 출력합니다.
    $('#article').printOut();
    #('.lst').printOut(); // 첫번째 .lst 의 내용만 출력됨 T_T;;;
});
</script>

모든 .lst 에 대해서 동작하도록 수정해보겠습니다.


<script>
(function($) {
    // `printOut` 이라는 jQuery 플러그인 등록
    $.fn.printOut = function() {
        // 아래의 this는 $('.lst') 와 같습니다.
        // '.lst' 클래스를 갖는 element 들에 대해서 반복문을 돈다고 이해하시면 되겠습니다.
        this.each(function(idx, ele) {
            // 각 .lst ELEMENT 들에 대해서 내용을 화면에 출력
            console.log($(ele).html());
        }
    };
})(jQuery);
$(document).ready(function () {
    // 3개의 LI 의 내용들이 콘솔에 출력됩니다.
    $('.lst').printOut();
});
</script>

만약, '.lst' 의 내용을 화면에 출력하고 이어서 다른 플러그인을 호출하고 싶다면 어떻게 하는지 해보겠습니다.


<script>
(function($) {
    // `printOut` 이라는 jQuery 플러그인 등록
    $.fn.printOut = function() {
        // `return` 이 추가되었습니다.
        // 여기서 return 을 하므로, 다음과 같은 호출이 가능합니다.
        // $(selector).printOut().setBorder();
        return this.each(function(idx, ele) {
            console.log($(ele).html());
        }
    };
    // 선택된 HTML Element 에 red border 를 설정하는 플러그인
    $.fn.setBorder = function() {
        return this.each(function (idx, ele) {
            $(ele).css({'border': '1px solid red'});
        });
    };
})(jQuery);
$(document).ready(function () {
    // 3개의 LI 의 내용들이 콘솔에 출력됩니다.
    // 그리고 각 LI 에 border 가 red 로 설정됩니다.
    $('.lst').printOut().setBorder();
});
</script>
만약, `setBorder` 플러그인에 색을 지정하고 싶은경우 어떻게 하는지 해보겠습니다.

<script>
(function($) {
    $.fn.printOut = function() {
        return this.each(function(idx, ele) {
            console.log($(ele).html());
        }
    };
    // options 를 파라미터로 받습니다.
    $.fn.setBorder = function(options) {
        // 기본 설정값은 'border_color' 가 'red' 로 되어있습니다.
        // 이 설정을 파라미터로 넘어온 options 와 병합합니다.
        // 즉, 기본값은 'red' 이지만 사용자가 options 를 통해 색을 변경할 수 있습니다.
        let settings = $.extend({
            border_color: 'red'
        }, options);
        return this.each(function (idx, ele) {
            // `settings.border_color` 로 border 를 설정합니다.
            $(ele).css({'border': '1px solid '+settings.border_color});
        });
    };
})(jQuery);
$(document).ready(function () {
    // setBorder 플러그인에 {border_color: 'red'} 를 넘깁니다.
    // 위의 setBorder(options) 의 `options` 가 {border_color: 'red'} 인거죠.
    // 이렇게 하면, 화면에 LI 내용들이 3개 출력되고 각 LI 들에 파란색 border 가 생깁니다.
    $('.lst').printOut().setBorder({border_color: 'blue'});
});
</script>

쉬운 예제로 설명드렸습니다. 찬찬히 보시면 아실 수 있을겁니다.
답변으로 드린 예제도 형식은 지금 설명드린 플러그인 작성법에 따른것이므로 이해하실 수 있을겁니다.
화이팅~

답변 정말 감사드립니다.
이렇게 자세히 풀이해주시다니 감동입니다.^^
추가 질문이 있습니다.

let settings = $.extend({
            border_color: 'red' }, options);
options매개변수와 border_color:red를 합치는건데요
options가 border_color:'red' 보다 뒤에 있어서 결과적으로 뒤에있는 블루색상이 적용되는게 맞는지 궁금합니다.


그리고
              // $(this) 는 $(메뉴) 에 해당함
              $(this).each(function (idx, ele) {

this.each에서 this = $('.tabMenu')이며
  $(this).each(function (idx, ele) {에서 $(this)는 $(메뉴) 에 해당한다고 하셨는데요 어떻게 $(메뉴)가 되는지 모르겠습니다. 정확히 어떤 것인지도 모르겠습니다.


그리고 
      myTab함수에서       

 return this.each(function() {
              // $(this) 는 $(메뉴) 에 해당함
              $(this).each(function (idx, ele) { ~ blah ~ blah~

리턴을 해주기때문에 $('.tabMenu').myTab({ 에서 메소드 체이닝이 가능했던건데요
제가 맨 처음에 올렸던 질문 중에
  tabMenu1.$tabMenu.on("tabSelect", function(e) {
여기서도 체이닝이 일어납니다.

function tabMenu(selector) {에서

return {
    $tabMenu : $tabMenu 를 객체 키,값을 리턴 해주기 때문에
---------------------------------------------------------
tabMenu 함수는 tabMenu1에 대입되었습니다.
    var tabMenu1 = tabMenu("#tabMenu1");
---------------------------------------------------------
tabMenu1에서 .(object access operator)로 접근이 가능했던게 맞지 궁금합니다.

답변을 작성하시기 전에 로그인 해주세요.
전체 936
QA 내용 검색

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT