写一个jquery插件

第一次写jquery插件,在这里做一个记录和积累。

写jQuery插件的目的

主要是对一些重复使用率高的一系列方法或函数做封装,能够提高开发的效率,也方便后期维护

写插件前需要知道的预备知识

jQuery插件有哪几种类型?

  1. 封装对象方法的插件(对象级别)

    这类插件是最常见的一种,即将对象方法进行封装,然后通过选择器获取的jQuery对象进行操作。
    用法:$('#id').myPlugin()

  1. 封装全局函数的插件(类级别)

    即将独立的函数加到jquery命名空间下, 拓展jQuery类,典型例子$.ajax()
    用法:$.myPlugin()

jQuery插件中的闭包

常见的jQuery插件形式如下:

1
2
3
4
//为了更好的兼容性,开始有个分号
;(function ($) {
/*你的插件代码*/
})(jQuery);

利用闭包的特性,避免内部变量影响全局空间,通过形参$将jQuery传递给匿名函数,在插件内部就可以使用$作为jQuery的别名。

jQuery插件的机制

1
2
3
4
//扩展第一种类型(即对象级别)的插件
jQuery.fn.extend()
//扩展第二种类型(即类级别)的插件
jQuery.extend()

extend()方法接受一个Object类型的参数,key值为函数名或方法名,value值为函数主体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//给jQuery对象添加方法,就是对jQuery.prototype扩展,给jQuery类添加成员方法
$.fn.extend({
getInputText: function(){
$(this).click(function(){
alert($(this).val());
});
}
});
//$("#username").getInputText();

//给jQuery类扩展,相当于给jQuery添加静态方法
$.extend({
  add: function(a, b){return a + b;}
});
// $.add(1, 2); //return 3

除了可以扩展jQuery对象,$.extend(default, options)还可以扩展Object对象,用传入的参数options覆盖默认值default。

1
2
3
4
5
6
7
8
9
10
11
function myPlugin(options) {
var defaults = {
color: 'blue',
width: '100'
};
options = $.extend(defaults, options);
}

//myPlugin({color: 'white'});
//myPlugin({color: 'white', width: '200'});
//myPlugin();

需要注意的问题

  • 插件名推荐命名为jquery.插件名.js,比如jquery.myPlugin.js

  • 插件内部this的指向为将要执行的jquery对象,而在其他包含callback的jQuery函数里,this指向原生DOM元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function($) {
$.fn.m​​yPlugin = function () {

//此处没有必要将this包在$号中如$(this),因为this已经是一个jQuery对象。
//$(this)等同于 $($('#element'));

this.fadeIn('normal', function () {

//此处callback函数中this关键字代表一个DOM元素

})
;


};

})
(jQuery);

  • 用this.each对所有元素进行遍历,同时为了保持插件的链式调用,确保插件返回this关键字
1
2
3
4
5
6
7
8
9
10
(function($) {
$.fn.m​​yPlugin = function () {
return this.each(function () {

//此处是你的插件代码

});

};
})(jQuery);
  • 保护好默认参数

回顾上面讲$.extend(default, options)时举的例子

1
2
3
4
5
6
7
function myPlugin(options) {
var defaults = {
color: 'blue',
width: '100'
};
options = $.extend(defaults, options);
}

这种写法不好,调用extend时会改变defaults的值,defaults作为插件默认值应维持原状,若后续想再使用默认值,会发现它已被用户传来的值所更改。
所以更好的写法是把一个新的空对象作为extend的第一个参数,接下来是defaults和options,那么所有值合并后保存到了这个空对象上,保护了默认值。

1
2
3
4
5
6
7
function myPlugin(options) {
var defaults = {
color: 'blue',
width: '100'
};
options = $.extend({}, defaults, options);
}

动手编写一个jQuery插件

以分页插件作为练习,体会了上面的知识点,部分地方还有待优化

首先是HTML结构

1
2
3
//要插入页码的容器
<div id="pager"></div>
<p class="page-text"></p>

CSS结构

1
2
3
4
5
6
7
8
9
10
* {margin:0;padding: 0;}
ul {list-style: none;}

.page {height:40px;overflow: hidden;margin-top: 20px;}
.page li {display: inline-block;*display: inline;*zoom:1;border: 1px solid #CCC;background-color: #FFF;margin-right: 5px;padding: 5px 8px;cursor: pointer;}
.page li:hover {border:1px solid #1B4CA6;background-color: #BBD1F9;}
.page li.current {border:1px solid #1B4CA6;background-color: #BBD1F9;}
.page li.disable {border:1px solid #EEE;color:#999;cursor: auto;background-color: #FFF;}
.page-text {margin-top: 10px;}
.page-text strong {color: #78C6E2;margin-left: 3px;margin-right: 3px;}

js的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
;(function($) {

var defaults = {
pagecurrent: 1, //当前页码
pagecount:1, //页码总数
first_text: '首页', //首页按钮的文字可自定义
prev_text: '上一页', //上一页按钮的文字可自定义
next_text: '下一页', //下一页按钮的文字可自定义
last_text: '尾页', //尾页按钮的文字可自定义
max_per_page: 10 //每屏最多显示多少个页码
};

$.fn.pager = function(options) {
var options = $.extend({}, defaults, options);

return this.each(function() {
$(this).empty().append(renderPage(options));
});
}

function renderPage(opts) {
var pagecurrent = parseInt(opts.pagecurrent);
var pagecount = parseInt(opts.pagecount);
var btncallback = opts.btncallback;
var max_per_page = parseInt(opts.max_per_page);
var first_text = opts.first_text;
var prev_text = opts.prev_text;
var next_text = opts.next_text;
var last_text = opts.last_text;
//页码wrap
var pageWrap = $('<ul class="page"></ul>');
//添加第一页和上一页按钮
pageWrap.append(renderBtn('first', first_text, pagecurrent, pagecount, btncallback))
.append(renderBtn('prev',prev_text, pagecurrent, pagecount, btncallback));

//控制一屏最多显示n条页码
var startNum = 1;
var endNum = max_per_page;
var point = Math.ceil((endNum - startNum) / 2);

if (pagecurrent > point) {
startNum = pagecurrent - point;
endNum = pagecurrent + point;
}

if (endNum > pagecount) {
startNum = pagecount - max_per_page + 1;
endNum = pagecount;
}

if (startNum < 1) {
startNum = 1;
}
//渲染页码列表
for (var pagenumber = startNum; pagenumber <= endNum; pagenumber++) {
var pagebtn = $('<li>'+ pagenumber +'</li>');
if (pagenumber == pagecurrent) {
pagebtn.addClass('current');
}
else {
pagebtn.click(function() {
btncallback(this.innerHTML);
});
}
pageWrap.append(pagebtn);
}

//渲染下一页和尾页按钮
pageWrap.append(renderBtn('next', next_text, pagecurrent, pagecount, btncallback))
.append(renderBtn('last', last_text, pagecurrent, pagecount, btncallback));

return pageWrap;
}
//首页,上一页,下一页,尾页按钮的方法
function renderBtn(btntype, btntext, pagecurrent, pagecount, btncallback) {
var button = $('<li>'+ btntext +'</li>');

var destPage = 1;

switch (btntype) {
case 'first':
destPage = 1;
break;
case 'prev':
destPage = pagecurrent - 1;
break;
case 'next':
destPage = pagecurrent + 1;
break;
case 'last':
destPage = pagecount;
}

if (btntype == 'first' || btntype == 'prev') {
if (pagecurrent <= 1) {
button.addClass('disable');
}
else {
button.click(function() {
btncallback(destPage);
});
}
}
else {
if (pagecurrent >= pagecount) {
button.addClass('disable');
}
else {
button.click(function() {
btncallback(destPage);
});
}
}

return button;
}

})(jQuery);

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Google的需要翻一下墙,你懂的--
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="jquery.pager.js" type="text/javascript"></script>
<script type="text/javascript">
$(function(){
$('#pager').pager({
pagecurrent:1,
pagecount:20,
btncallback:pageClickCallback ,
//first_text: 'first'
});

});
pageClickCallback = function(pageclickednumber){
$('#pager').pager({
pagecurrent:pageclickednumber,
pagecount:20,
btncallback:PageClick,
//first_text: 'first'
});
$('.page-text').html('当前是第<strong>'+pageclickednumber+'</strong>页');
}
</script>

效果请查看 demo