들어가기 전

 

강좌를 바로 바로 올려야 하는데 마지막 강좌 후에 가장 중요한 jquery plugin작성법은 정작 하지 않은 상태에서 잠수 탄 것 처럼 오랫동안 강좌를 못올려서 죄송합니다.

 

일때문이라는 말도 안되는 변명 보다는 그냥 좀 노느라.. 조금 놀다 오느라...

기다리신 분없겟지만 늦어서 죄송합니다. ( 원맨쑈~~~!!)

 

jQuery plugin이란 것은

 

이미 instance화 되어진 object인 jQuery object를 확장하여 새로운 function을 만들어서 사용할 수 있게 하는 것을 의미합니다.

 

이전 강좌에서 배웠듯이 간단하게 instance화된 새로운 함수 명에 새로운 함수를 작성하면 된다고 볼 수 있다.

 

하지만 jQuery object의 강점중에 하나인 selector로 반환되어져오는 jQuery object를 그대로 사용할 수 있는 형태여야 할 것이다.

 

그래서 미리 만들어진 방법인 $.fn 이라는 namespace밑에 확장해주면 원하는 결과를 얻을 수 있다.

 

이렇게 보면 jQuery plugin의 작성이라는 건 굉장히 단순해 보인다. 실제로도 그렇다.


하지만 특정 plugin을 작성하려면 많은 methods, event procedures 그리고 속성 등을 사용할 수 있어야 편리할 것이다.

 

그래서 jQuery org에서 말하는 plugin 작성법을 기준으로 몇가지를 숙지해 두는 편이 나은 방법인 것과 공유되질 수있는 방법론이 될 것이다.

 

1. $.fn을 확장하면 $ jQuery object를 활용하여 사용할 수 있는 plugin 작성가능하고 $.fn은 jQuery effin'이라고 읽어라는 군요. 제이쿼리 에핀 이렇게 읽으면 되는 건가?(영어가 편한 언어가 아니라. 아픔이 큽니다.)

 

 

2. 외부 코드와 분리해주시기 위해서 self-invating function을 이용하여 작성하자.

 

 

3. method의 호출 결과로 반드시 특정 object나 value를 반환해주는 것이 아니라면(왠만하면 그러지 않는 것이 좋을 것다.) jQuery에 chainability(사전에도 없는 말이다.
   굳이 한글로 바꾸자면 연쇄적으로 내지는 연속적으로 호출가능한 형태)를 유지해주자.

 

 

4. 동작에 필요한 defaults 값들을 object정의하고 외부에서 받아올 options object을 받아서 $.extend()로 확장하여 사용한다.

 

 

5. 하나의 plugin이라도 수많은 methods를 다른 함수로 일일히 작성하지 말고 하나의 plugin함수 안에서 paramter를 받아서 내부에서 분기하도록 하자.

 

 

6. bind 등(애매한 표현 등... 하하 이벤트에 procedure를 붙이는 방법이 다른 좋은 방법들과 이슈가 되는 많다고 하니 이렇게 애매하게 표현하는게 상책이죠.)을 통해서 event procedure를 호출하려고 할때에 다른 plugin과 구분하거나 한번에 unbind하기 위해서라도 plugin이름을 활용하여 명명하자.

 

 

7. 일반적으로 object의 속성과 비교하면 상이한 점이 꽤나 있다.
   하지만 이 instance화되어져 있는 jQuery의 plugin으로서 마찬가지 instance화되어져 있는 속성이라는 것이 각 개체의 특정 값을 유지하거나 공유하기 위해서라면
   이걸 jQuery에서는 data라고 하고 data메서드를 활용하여 작성한다.
   여기서 마찬가지 하나 data메서드, 결국 하나의 data라는 object인데 그 하위에 plugin과 같은 이름 명명하고 그 하위에  값이나 개체의 형태로 data안에 해당 plugin의 속성을 작성해준다.


위의 방법이 반드시 강제성을 띠는 것은 아니다. 단지 가이드 라인이라고 해야할 것이다.


필요하고 더 나은 방법이 있다면 다른 방식으로 구현하고 그 방법이 실제로 더 나은 방벙이라면 역으로 제시하여 새로운 가이드가 만들어질 수도 있을 것이다.

 

이제 위의 항목들을 하나 하나 살펴보도록 하자.

 

 

1. $.fn을 확장하면 $ jQuery object를 활용하여 사용할 수 있는 plugin 작성가능하고 $.fn은 jQuery effin'이라고 읽어라는 군요. 제이쿼리 에핀 이렇게 읽으면 되는 건가?(영어가 편한 언어가 아니라. 아픔이 큽니다.)

<html>
<head>
<title>Javascript Sample Page - jQuery plugin basic</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
/*
가장 기본적인 형태의 jQuery plugin으로 jQuery instance의 $.fn을 확장하면 jQuery object를 확장하여 plugin으로 사용할 수 있다.
*/
$.fn.GetInnerText = function () {
this.each(function () { alert($(this).text()); });
}
$(document).ready(
function () { $(".title").GetInnerText(); }
)
</script>
</head>
<body>
<table>
<tr>
<td class="title">제목</td>
<td>진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td>김소월</td>
</tr>
</table>
</body>
</html>


 

 


2. 외부 코드와 분리해주시기 위해서 self-invating function을 이용하여 작성하자.

 

<html>
<head>
<title>Javascript Sample Page - jQuery plugin standard</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
/*
좀 더 복잡한 형태의 jQuery plugin 으로 사용되면서는 private function과 private 변수의 사용이 불가피해진다.
그러므로 다른 global 코드와 이름 중복으로 발생할 문제를 해결하기 위해서 jQuery를 parameter로 하는 self-invacation function을 사용해줍니다.
*/
(
function ($) {
$.fn.GetInnerText = function () {
this.each(function () { alert($(this).text()); });
}
}
)($) 
$(document).ready(
function () { $(".title").GetInnerText(); }
)
</script>
</head>
<body>
<table>
<tr>
<td class="title">제목</td>
<td>진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td>김소월</td>
</tr>
</table>
</body>
</html>

 


자 그러므로 가장 단순한 형태의 jQuery Plugin 작성형태라고 하면 "(function ($){ $.fn.myplugin = function () { } })($)"이 될 것이다.

 

 

3. method의 호출 결과로 반드시 특정 object나 value를 반환해주는 것이 아니라면(왠만하면 그러지 않는 것이 좋을 것다.) jQuery에 chainability(사전에도 없는 말이다.
   굳이 한글로 바꾸자면 연쇄적으로 내지는 연속적으로 호출가능한 형태)를 유지해주자.

 

<html>
<head>
<title>Javascript Sample Page - Chainability 1 </title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
/*
예제에서 파악할 수 있듯이 기 구현되어있는 대부분의 jQuery의 function은 jQuery개체를 최종적으로 계속 반환함으로써 연쇄적인 호출이 가능한 형태로 구현되어져 있습니다.
*/
(
function ($) {
$.fn.GetInnerText = function () {
this.each(function () { alert($(this).text()); });
}
}
)($)
$(document).ready(
function () {
$(".title").css("background-color", "#eeeeee").GetInnerText();
$(".title").GetInnerText().css("background-color", "#eeeeee"); // css method 에러 발생
}
)
</script>
</head>
<body>
<table>
<tr>
<td class="title">제목</td>
<td>진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td>김소월</td>
</tr>
</table>
</body>
</html> 

 

 

 

 

그럼 chainability하게 작성하는 방법에 관해서 알아보자.

 

<html>
<head>
<title>Javascript Sample Page - Chainability 2 </title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
/*
chainability를 유지해주기. chainability(사전에도 없는 걸 보면 지어낸 말이거나 아니면 신조어일까요?)
*/
(
function ($) {
$.fn.GetInnerText = function () {
return this.each(function () { alert($(this).text()); });
}
}
)($)
$(document).ready(
function () {
$(".title").GetInnerText().css("background-color", "#eeeeee");
//$("td").each(function () {$(this).width();});
}
)
</script>
</head>
<body>
<table>
<tr>
<td class="title">제목</td>
<td>진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td>김소월</td>
</tr>
</table>
</body>
</html> 

 

 

 

 

적절한 예제라고 보기에는 억지가 많지만 작성법만을 보도록 하자.

결국 마지막에 instance화된 자신의 object를 의미하는 this를 반환하면 된다.

 

 


4. 동작에 필요한 defaults 값들을 object정의하고 외부에서 받아올 options object을 받아서 $.extend()로 확장하여 사용한다.

 

<html>
<head>
<title>Javascript Sample Page - defaults and options with $.extend </title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
//$.extend를 이용하여 좀 더 사용하기 편리한 parameters와 default값들을 지정하기
(
function ($) {
// plugin내에서 사용할 기본값으로 setting된 object를 미리 만들어 둡니다.
StylingDefaults = { font: "맑은 고딕", foreColor: "blue", backColor: "#eeeeee" }; 

$.fn.MyCompanyStyling = function (options) {
this.each(
//function (options) { //이것은 each라는 메서드가 넘겨주는 첫번째 parameter로 options은 each의 각 index가 넘어옵니다.
function () {
// plugin 내의 기본값을 가진 object를 기준으로 받은 options을 덮어 씁니다.
$.extend(StylingDefaults, options);
//$.extend(options, StylingDefaults); // 이렇게 하면 받은 options 값을 기준으로 원래 기본값을 덮어쓰면서 기본값이 적용됩니다.

//원하는 코드를 작성합니다.
$(this).css("font-family", StylingDefaults.font).css("color", StylingDefaults.foreColor).css("background-color", StylingDefaults.backColor);
}
);
return this;
}
}
)($)

$(document).ready(
function () {
$(".title").MyCompanyStyling({ foreColor:"red" });
}
)
</script>
</head>
<body>
<table>
<tr>
<td class="title">제목</td>
<td>진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td>김소월</td>
</tr>
</table>
</body>
</html>

 


 

 

 


5. 하나의 plugin이라도 수많은 methods를 다른 함수로 일일히 작성하지 말고 하나의 plugin함수 안에서 paramter를 받아서 내부에서 분기하도록 하자.

 

<html>
<head>
<title>Javascript Sample Page - Methods Namespacing</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
// 근본적으로 jQuery plugin이라는 것이 fn이라는 namespace 아래에 자리 잡는 것입니다.
// 그래서 plugin 하나 하나가 다수의 method를 만들면 결국 plugin 몇 개만 써도 엄청난 수의 method가 fn밑에 자리 잡게 되고
// 결국 어떤 plugin의 메서드인지 뭐하는 method인지 헤갈리게 될 가능성이 높을 것 같습니다.
// 그래서 jQuery org는 method이름을 paramter의 형태로 받아서 해당 plugin 내부에서 분기하라고 가이드 하고 있습니다. 
(
function ($) {

// 사용할 method 선언부
var actions = {
TitleStyling: function (options) {
Defaults = { font: "맑은 고딕", foreColor: "blue", backColor: "#eeeeee" };
this.each(
function () {
$.extend(Defaults, options);
$(this).css("font-family", Defaults.font).css("color", Defaults.foreColor).css("background-color", Defaults.backColor);
}
);
return this;
},
ValueCellStyling: function () { this.each(function () { $(this).css("font-weight", "normal"); }); return this; }
}

// 각각의 sub procedure를 호출해주는 main
$.fn.MyCompanyStyling = function (action) {
if (actions[action])
return actions[action].apply(this, Array.prototype.slice.call(arguments, 1));
else
return actions.TitleStyling.apply(this, arguments);
}
}
)($)

$(document).ready(
function () {
$(".title").MyCompanyStyling({ foreColor: "red" });
$(".cellcontent").MyCompanyStyling("ValueCellStyling");
}
)
</script>
</head>
<body>
<table>
<tr>
<td class="title">제목</td>
<td class="cellcontent" style="font-weight:bold">진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td class="cellcontent">김소월</td>
</tr>
</table>
</body>
</html>


 

 


6. bind 등(애매한 표현 등... 하하 이벤트에 procedure를 붙이는 방법이 다른 좋은 방법들과 이슈가 되는 많다고 하니 이렇게 애매하게 표현하는게 상책이죠.)을 통해서 event procedure를 호출하려고 할때에 다른 plugin과 구분하거나 한번에 unbind하기 위해서라도 plugin이름을 활용하여 명명하자.

 

 
<html>
<head>
<title>Javascript Sample Page - Events Namespacing</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
(
function ($) {
var actions = {
// Event Procedure를 각각의 events에 bind해주는 역활의 함수
// 일반적으로 jQuery org예제처럼 plugin이 초기화되면 해당 동작을 많이 하므로 init이라는 이름으로 만들고 초기화하면서 bind해주는 경우가 많다.
// 본 예제에서는 이해를 돕기 위해 이름을 별나게 작성하였다.
AttachEvents: function () {
this.each(function () {
// 해당 plugin이름으로 하나씩 event bind해준다.
$(this).bind('click.myCompanyStyling', displayClickedCoordinate);
$(this).bind('mousemove.myCompanyStyling', displayMovingCoordinate);
});
return this;
}, 
// 필요할 경우나 개인적인 의견으로 한 페이지에서 끝나고 다음 페이지로 이동하는 웹페이지의 형태에서 잘 사용하지 않을 것 같으나 소멸자에서 사용이 되어진다.
// unbind해주는 method로 소멸자의 경우 일반적으로 destory라는 이름을 많이 사용한다.
// 본 예제에서는 이해를 돕기 위해 이름을 별나게 작성하였다.
DisattachEvents: function () {
// 해당 plugin 이름으로 한번 unbind
$(this).unbind('.myCompanyStyling');
return this;
}
}
// plugin 내부에 private한 함수들이다.
// self-invacation 되어 있으므로 현재 상태로는 외부에서 접근이 불가능한 코드이다.
function displayClickedCoordinate(e) {
$("#divClickedCoordinate").text('styling clicked coordinate x: ' + e.pageX + ', y: ' + e.pageY);
}
function displayMovingCoordinate(e) {
$("#divMovingCoordinate").text('mouse coordinate x: ' + e.pageX + ', y: ' + e.pageY);
}
// method로 분기해주는 main procedure
$.fn.MyCompanyStyling = function (action) {
if (actions[action])
return actions[action].apply(this, Array.prototype.slice.call(arguments, 1));
else
return actions.init.apply(this, arguments);
}
}
)($)
$(document).ready(
function () {
// 위의 plugin의 namespace가 아닌 다른 곳에서의 bind 2가지
// 1. window의 mousemove bind
$(window).bind('mousemove', function (e) {
$("#divGlobalMousePosition").text('mouse coordinate x: ' + e.pageX + ', y: ' + e.pageY);
});
// 2. plugin과 같은 개체의 같은 event에 bind
$('#tableContent').bind('click', function (e) {
$("#divClickedCoordinate").text('clicked coordinate x: ' + e.pageX + ', y: ' + e.pageY);
});
}
);
</script>
</head>
<body>
<table id="tableContent">
<tr>
<td class="title">제목</td>
<td class="cellcontent" style="font-weight:bold">진달래</td>
</tr>
<tr>
<td class="title">시인</td>
<td class="cellcontent">김소월</td>
</tr>
</table>
<div id="divGlobalMousePosition"></div>
<div id="divClickedCoordinate"></div>
<div id="divMovingCoordinate"></div>
<!-- plugin을 통한 이벤트 프로시저 bind -->
<input type="button" value="attach events" onclick="javascript:$('#tableContent').MyCompanyStyling('AttachEvents');" />
<!-- plugin을 통한 이벤트 프로시저 unbind -->
<input type="button" value="disattach events" onclick="javascript:$('#tableContent').MyCompanyStyling('DisattachEvents');" />
</body>
</html>
 

 

 


7. 일반적으로 object의 속성과 비교하면 상이한 점이 꽤나 있다.
   하지만 이 instance화되어져 있는 jQuery의 plugin으로서 마찬가지 instance화되어져 있는 속성이라는 것이 각 개체의 특정 값을 유지하거나 공유하기 위해서라면
   이걸 jQuery에서는 data라고 하고 data메서드를 활용하여 작성한다.
   여기서 마찬가지 하나 data메서드, 결국 하나의 data라는 object인데 그 하위에 plugin과 같은 이름 명명하고 그 하위에  값이나 개체의 형태로 data안에 해당 plugin의 속성을 작성해준다.

 

 

먼저 간단하게 table cell 까만 막대기를 그려주는 jQuery plugin을 작성법대로 기본을 잡아주자.

 

 
<html>
<head>
<title>Javascript Sample Page - Events Namespacing</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
(
function ($) {
var methods = {
init: function () {
this.each(function () {
$(this).append($('<div style=\"background-color:black;height:15px;width:100px;\">&nbsp;</div>'));
});
return this;
},
} 
$.fn.RandomBar = function (method) {
if (methods[method])
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
else
return methods.init.apply(this, arguments);
}
}
)($)
$(document).ready(
function () {
$(".sample tr td").RandomBar();
}
);
</script>
</head>
<body>
<table class="sample">
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
</table>
</body>
</html>
 

 

 

 

자! 이제 랜덤한 길이로 보이도록 하자.
(참고로 차분히 이해를 돕기 위해서 step별로 하는 것이지 절대로 원고 불리기가 아닙니다. ㅡㅡ;)

 

 
<html>
<head>
<title>Javascript Sample Page - Events Namespacing</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
(
function ($) {
var methods = {
init: function () {
this.each(function () {
$(this).append($('<div style=\"background-color:black;height:15px;width:' + Math.floor(Math.random()*1001).toString() + 'px;\">&nbsp;</div>'));
});
return this;
}
} 
$.fn.RandomBar = function (method) {
if (methods[method])
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
else
return methods.init.apply(this, arguments);
}
}
)($)
$(document).ready(
function () {
$(".sample tr td").RandomBar();
}
);
</script>
</head>
<body>
<table class="sample">
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
</table>
</body>
</html>
 

 

 

 

자! 그럼 이제 길이과 미리 정해진 색상과 라벨을 할당하고 이에 관한 속성을 만들어 보도록 하겠습니다.

 

 
<html>
<head>
<title>Javascript Sample Page - Events Namespacing</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
(
function ($) {
// private variables
var barnames = ['사랑', '평온', '행복']
var barcolors = ['red', 'blue', 'gray', 'black', 'yellow'] 
var methods = {
init: function () {
this.each(function () {
// set data
$(this).data('RandomBar', {
name: barnames[Math.floor(Math.random() * 3) - 1],
barcolor: barcolors[Math.floor(Math.random() * 6) - 1],
barlength: Math.floor(Math.random() * 1001).toString() + 'px'
});
// get data
$(this).append($('<div style=\"background-color:' + $(this).data().RandomBar.barcolor + ';height:15px;width:' + $(this).data().RandomBar.barlength + ';\">&nbsp;</div>'));
});
return this;
}
}

$.fn.RandomBar = function (method) {
if (methods[method])
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
else
return methods.init.apply(this, arguments);
}
}
)($)
$(document).ready(
function () {
$(".sample tr td").RandomBar();
}
);
</script>
</head>
<body>
<table class="sample">
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
</table>
</body>
</html>
 

 

 

 

이렇게 만들어진 속성에 해당하는 data로 유지된 value를 event procedure에 활용하는 형태로 마무리해보겠습니다.

 

 
<html>
<head>
<title>Javascript Sample Page - Events Namespacing</title>
<script language="javascript" type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script language="javascript" type="text/javascript">
(
function ($) {
// private variables
var barnames = ['사랑', '평온', '행복']
var barcolors = ['red', 'blue', 'gray', 'black', 'yellow'] 
//private event procedure
function displayBarInfo() {
$(".barinfo").text('이름: ' + $(this).data().RandomBar.name + ', 길이: ' + $(this).data().RandomBar.barlength + ', 색상: ' + $(this).data().RandomBar.barcolor);
}
function hideBarInfo() {
$(".barinfo").text('');
}
var methods = {
init: function () {
this.each(function () {
// set data
$(this).data('RandomBar', {
name: barnames[Math.floor(Math.random() * 3)],
barcolor: barcolors[Math.floor(Math.random() * 5)],
barlength: Math.floor(Math.random() * 1001).toString() + 'px'
});
// get data
$(this).append($('<div style=\"background-color:' + $(this).data().RandomBar.barcolor + ';height:15px;width:' + $(this).data().RandomBar.barlength + ';\">&nbsp;</div>'));
// attach event procedure
$(this).bind('mouseover.Randombar', displayBarInfo);
$(this).bind('mouseout.Randombar', hideBarInfo);
});
return this;
}
}

$.fn.RandomBar = function (method) {
if (methods[method])
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
else
return methods.init.apply(this, arguments);
}
}
)($)
$(document).ready(
function () {
$(".sample tr td").RandomBar();
}
);
</script>
</head>
<body>
<!-- 절대 분량을 늘릴려고 줄 수 많이 한 것 아닙니다. 재미있어요. 하하하 -->
<table class="sample">
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td></td>
</tr>
</table>
<div class="barinfo"></div>
</body>
</html>
 

 

 

마치며

 

 

이후 jQuery plugin 강좌는 특별한 계기가 없는 계획이 없으며 보내주시는 피드백을 보아서 jQuery Plugin 활용예제나 되짚어 상세히 보기 편을 만들 수 있으면 해보도록 하겠습니다. (아마도 가능성이 낮겠죠. 하하)

 

 

감사합니다.