Write your first jQuery plugin
一般来说,jQuery插件的开发分为两种:一种是挂在jQuery命名空间下的全局函数,也可称为静态方法;另一种是jQuery对象级别的方法,即挂在jQuery原型下的方法,这样通过选择器获取的jQuery对象实例也能共享该方法。
一、在讲解jQuery插件基本结构和模式前,先介绍下两个重要的方法,还有不知啥原因,代码无法折叠,导致整体篇幅稍微有点长,阅读时请加点耐心:
1、$.extend(target, [object1], [objectN])
该方法主要用于合并两个或更多对象的内容(属性)到第一个对象,并返回合并后的第一对象。如果该方法只有一个参数target,则该参数将扩展jQuery的命名空间,即作为静态方法挂在jQuery全局对象下,如jQuery里的$.ajax、$.getJSON全局函数等:
|
1
2
3
4
|
// 将hello方法挂在jquery全局对象下,作为静态方法$.extend({ hello: function() {alert("hello");}}); |
又如,在jQuery全局对象中扩展一个hcy命名空间:
|
1
|
$.extend({ hcy: { } }); |
并将hello方法扩展到之前扩展的JQuery的hcy命名空间中去:
|
1
2
3
|
$.extend($.hcy, { hello: function() {alert("hello");}}); |
在开发插件时,也可以将方法扩展到jQuery的原型中去,如:
|
1
2
3
|
$.extend($.fn, { hello: function() {alert("hello");}}); |
值得注意的是:多个对象参数合并时,会破坏第一个对象的结构,所以可传递一个空对象作为第一个参数,如:$.extend({}, object1, object2);
另外,对于深度拷贝,即如果对象中也嵌套子对象,则会拷贝并覆盖子对象(如果有同名属性)全部的属性,需要设置第一个参数deep为true值,如:$.extend(true, target, object1, [objectN])。
2、$.fn.extend(target)
在jQuery中,$.fn本质上是等于jQuery的原型,即$.fn = $.prototype, 所以该方法实际上是为jQuery原型添加方法,即把target对象的方法添加到jQuery原型中去,这样jQuery对象实例就可以访问添加的方法了,这也是jQuery插件开发常用的方法,特别是添加多个接口时。如:
|
1
2
3
4
5
|
// 将hello、hello2方法添加到jquery原型中$.fn.extend({ hello: function() {alert("hello!");}, hello2: function() {alert("hello again!);}}); |
如果添加单个方法到jQuery原型中,可使用$.fn.pluginName方法添加,如:
|
1
2
3
4
|
// 将hello方法添加到jquery原型中$.fn.hello = function() { // ...}; |
二、在开发过一些 jQuery 插件后,慢慢的探索出了一套开发jQuery插件的基本结构和模式。这样在面对复杂多变的需要方案时,只要专注最主要的逻辑代码就行了。另外,使用相同的设计模式和架构也让修复bug或者二次开发更容易。
在这里分享一些平时遇到和总结的经验:
1、把全部代码放在闭包(一个即时执行函数)里
此时闭包相当于一个私有作用域,外部无法访问到内部的信息,并且不会存在全局变量的污染情况。官方创建开发规范的解释是:a) 避免全局依赖;b) 避免第三方破坏;c) 兼容jQuery操作符’$'和’jQuery ‘。如下所示:
|
1
2
3
4
|
(function($) { // 局部作用域中使用$来引用jQuery // ...})(jQuery); |
这段代码在被解析时可理解成以下代码:
|
1
2
3
4
|
var jQ = function($) { // code goes here};jQ(jQuery); |
2、提供插件的默认参数选项
一个扩展性良好的插件应该是可以让使用者根据需求自定义参数选项,并控制插件的行为,所以提供恢复默认选项是很有必要的。你可以通过jQuery的extend方法来设置这些选项:
|
1
2
3
4
5
6
7
8
|
var defaults = { name: "hcy", age: 22, job: "student", walk: function() { // ... }}; |
$.extend({}, defaults, options || {});
注:参数选项设置时也可以使用如下模式,即把参数对象挂在插件命名空间下:
|
1
2
3
4
5
6
7
8
|
$.fn.pluginName.defaults = { name: "hcy", age: 22, job: "student", walk: function() { // ... }}; |
3、遍历多个元素并返回
jQuery使用Sizzle选择器引擎,Sizzle可以为你的函数提供多元素操作(例如对所有类名相同的元素)。这是jQuery几个优秀的特性之一,在开发插件过程中即使你不准备为你的插件提供多元素支持,但为这做准备仍然是一个很好的实践。另外,jQuery有一个很好的特点就是可以进行方法级联,也可称为链式调用,所以我们不应该破坏这个特性,始终在方法中返回一个元素。如:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
function($) { // 参数选项设置... // 向jQuery原型中添加你的插件代码,用“pluginName”作为插件的函数名称。 $.fn.pluginName = function(options) { // 遍历匹配的元素||元素集合,并返回this,以便进行链式调用。 return this.each(function() { // 此处可通过this来获得每个单独的元素(jQuery对象) var $this = $(this); }); };})(jQuery); |
4. 一次性代码放在主循环以外
这一条很重要,但是常常被忽略。简单的讲,如果你有一段代码是一堆默认值,只需要被实例化一次,而不是每次调用你插件功能的时候都需要实例化,你应该把这段代码放在插件方法的外面。这样可以让你的插件运行的更高效,节省内存。如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
function($) { // 参数选项设置 var defaults = { name: "hcy", age: 22, job: "student", walk: function() { // ... } }; // 向jQuery原型中添加你的插件代码,用“pluginName”作为插件的函数名称。 $.fn.pluginName = function(options) { var opts = $.extend({}, defaults, options || {}); // 遍历匹配的元素||元素集合,并返回this,以便进行链式调用。 return this.each(function() { // 此处可通过this来获得每个单独的元素(jQuery对象) var $this = $(this); // ... }); };})(jQuery); |
5、定义公有方法和私有方法
一般情况下,对于一个jQuery插件,一个基本的函数就可以很好地工作,但是对于复杂一点的插件就需要提供各种各样的方法和私有函数。你可能会使用不同的命名空间去为你的插件提供各种方法,但是添加过多的命名空间反而会使代码变得混乱,健壮性下降。所以最好的解决办法是适当地定义私有函数和方法。例子如下所示:
|
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
|
(function($) { // 在我们插件容器内,定义一个私有方法 var privateFunction = function() { // code here }; // 通过字面量创造一个对象,存储我们需要的共有方法 var methods = { // 在字面量对象中定义每个单独的方法 init: function() { // 为了更好的灵活性,对来自主函数,并进入每个方法中的选择器其中的每个单独的元素都执行代码 return this.each(function() { // 为每个独立的元素创建一个jQuery对象 var $this = $(this); // 执行代码 例如:privateFunction() }); }, destroy: function() { // 对选择器每个元素都执行方法 return this.each(function() { // 执行代码 }); } }; $.fn.pluginName = function() { // 获取我们的方法,遗憾的是,如果我们用function(method){}来实现,这样会毁掉一切的 var method = arguments[0]; // 检验方法是否存在 if(methods[method]) { // 如果方法存在,存储起来以便使用 // 注意:我这样做是为了等下更方便地使用each() method = methods[method]; // 如果方法不存在,检验对象是否为一个对象(JSON对象)或者method方法没有被传入 } else if (typeof method === "object" || !method ) { // 如果我们传入的是一个对象参数,或者根本没有参数,init方法会被调用 method = methods.init; } else { // 如果方法不存在或者参数没传入,则报出错误。需要调用的方法没有被正确调用 $.error("Method" + method + "does not exist on jQuery.pluginName"); return this; } // 调用我们选中的方法 // 再一次注意我们是如何将each()从这里转移到每个单独的方法上的 return method.call(this); };})(jQuery); |
注意我把 privateFunction 当做了一个函数内部的全局变量。考虑到所有的代码的运行都是在插件容器内进行的,所以这种做法是可以被接受的,因为它只在插件的作用域中可用。在插件中的主函数(pluginName)中,我检验了传入参数所指向的方法是否存在。如果方法不存在或者传入的是参数为对象, init 方法会被运行。最后,如果传入的参数不是一个对象而是一个不存在的方法,我们会报出一个错误信息。
下面是一些用法的例子:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 为每个类名为 ".className" 的元素执行init方法$(".className").pluginName();$(".className").pluginName("init");$(".className").pluginName("init", {}); // 向init方法传入“{}”对象作为函数参数$(".className").pluginName({}); // 向init方法传入“{}”对象作为函数参数// 为每个类名为 “.className” 的元素执行destroy方法$(".className").pluginName("destroy");$(".className").pluginName("destroy", {}); // 向destroy方法传入“{}”对象作为函数参数// 所有代码都可以正常运行$(".className").pluginName("init", "argument1", "argument2"); // 把 "argument1" 和 "argument2" 传入 "init"// 不正确的使用$(".className").pluginName("nonexistantMethod");$(".className").pluginName("nonexistantMethod", {});$(".className").pluginName("argument1"); // 会尝试调用 "argument1" 方法$(".className").pluginName("argument1", "argument2"); // 会尝试调用 "argument1" ,“argument2”方法$(".className").pluginName("privateFunction"); // "privateFunction" 不是一个方法 |
注明:该例子代码参考伯乐在线的一篇文章。
6、添加持久性数据
在插件开发过程中,有时需要在插件中保存设置和信息,这时jQuery中的$.data函数就可以派上用场了。使用时,它会尝试获取和元素相关的数据,如果数据不存在,它就会创造相应的数据并添加到元素上。一旦你使用了$.data来为元素添加信息,请确认你已经记住remember,当不再需要数据的时候,用$.removeData函数来删除相应的数据。下面的例子也同样摘自伯乐在线一篇文章(深入理解jQuery插件开发)里的一小段:
|
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
|
(function($) { var privateFunction = function() { // 执行代码 } var methods = { init: function(options) { // 在每个元素上执行方法 return this.each(function() { var $this = $(this); // 尝试去获取settings,如果不存在,则返回“undefined” var settings = $this.data("pluginName"); // 如果获取settings失败,则根据options和default创建它 if (typeof settings === "undefined") { var defaults = { propertyName: "value", onSomeEvent: function() {} }; settings = $.extend({}, defaults, options); // 保存我们新创建的settings $this.data("pluginName", settings); } else { / 如果我们获取了settings,则将它和options进行合并(这不是必须的,你可以选择不这样做) settings = $.extend({}, settings, options); // 如果你想每次都保存options,可以添加下面代码: // $this.data("pluginName", settings); } // 执行代码 }); }, destroy: function(options) { // 在每个元素中执行代码 return $(this).each(function() { var $this = $(this); // 执行代码 // 删除元素对应的数据 $this.removeData("pluginName"); }); }, val: function(options) { // 这里的代码通过.eq(0)来获取选择器中的第一个元素的,我们或获取它的HTML内容作为我们的返回值 var someValue = this.eq(0).html(); // 返回值 return someValue; } }; $.fn.pluginName = function() { var method = arguments[0]; if (methods[method]) { method = methods[method]; arguments = Array.prototype.slice.call(arguments, 1); } else if (typeof method === "object" || !method ) { method = methods.init; } else { $.error("Method" + method + "does not exist on jQuery.pluginName"); return this; } return method.apply(this, arguments); }})(jQuery); |
在上面的代码中,首先检验元素的数据是否存在。如果数据不存在,“options”和“default”会被合并,构建成一个新的settings,然后用$.data()保存在元素中。
结语:好了,就这样结束先。
Write your first jQuery plugin的更多相关文章
- JQuery plugin ---- simplePagination.js API
CSS Themes "light-theme" "dark-theme" "compact-theme" How To Use Step ...
- jQuery plugin: Autocomplete 参数及实例
官网:jQuery plugin: Autocomplete (注:此插件已经不再更新.它的继任者是jQuery UI的一部分,) 此插件依赖于 jquery 1.2.6 --- j ...
- Signs of a poorly written jQuery plugin 翻译 (Jquery插件开发注意事项,Jquey官方推荐)
原文链接:http://remysharp.com/2010/06/03/signs-of-a-poorly-written-jquery-plugin/ 原文作者:remy sharp So far ...
- JQuery Plugin 1 - Simple Plugin
1. How do you write a plugin in jQuery? You can extend the existing jQuery object by writing either ...
- The ultimate jQuery Plugin List(终极jQuery插件列表)
下面的文章可能出自一位奥地利的作者, 列出很多jQuery的插件.类似的网站:http://jquerylist.com/原文地址: http://www.kollermedia.at/archiv ...
- [jQuery] 自做 jQuery Plugin - Part 1
有時候寫 jQuery 時,常會發現一些簡單的效果可以重複利用.只是每次用 Copy & Paste 大法似乎不是件好事,有沒有什麼方法可以讓我們把這些效果用到其他地方呢? 沒錯,就是用 jQ ...
- ollicle.com: Biggerlink – jQuery plugin
ollicle.com: Biggerlink – jQuery plugin Biggerlink – jQuery plugin Purpose Demo Updated for jQuery 1 ...
- Element DOM Tree jQuery plugin – Firebug like functionality | RockingCode
Element DOM Tree jQuery plugin – Firebug like functionality | RockingCode Element DOM Tree jQuery pl ...
- HTML5 Video player jQuery plugin
<!DOCTYPE html> <html lang="en" > <head> <meta charset="utf-8&qu ...
- JQuery Plugin 开发
学习 JQuery 插件开发之后, 可以将自己平时常用的功能封装成插件, 便于在不同的项目之间使用. JQuery 官网上的 插件开发教程就很不错, 简单易懂. 参考网址: http://learn. ...
随机推荐
- Mac os x 下配置Intellij IDEA + Tomcat 出现权限问题的解决办法
出现的错误提示如下: 下午9:11:27 All files are up-to-date下午9:11:27 All files are up-to-date下午9:11:27 Error runni ...
- CSS3 Media Query 响应式媒体查询
在CSS中,有一个极其实用的功能:@media 响应式布局.具体来说,就是可以根据客户端的介质和屏幕大小,提供不同的样式表或者只展示样式表中的一部分.通过响应式布局,可以达到只使用单一文件提供多平台的 ...
- File I/O的总结
1读写字符文件 BufferedReader br=new BufferedReader(new FileReader("文件路径")); BufferedWriter bw=ne ...
- 《深入理解java虚拟机》学习笔记之虚拟机即时编译详解
郑重声明:本片博客是学习<深入理解java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块 ...
- Mongodb 的劣势
MongoDB中的数据存放具有相当的随意性,不具有MySQL在开始就定义好了.对运维人员来说,他们可能不清楚数据库内部数据的数据格式,这也会数据库的运维带来了麻烦. 1. 事务关系支持薄弱.这也是所有 ...
- Kali Linux ettercap的使用
ettercap是执行ARP欺骗嗅探的工具,通常用它来施行中间人攻击. 我还介绍过另一个arp欺骗工具-arpspoof 我使用的是Kali Linux 2.0:在开始使用ettercap之前,先配置 ...
- [转载] PHP升级导致系统负载过高问题分析
原文:http://chuansongme.com/n/797172 背景 据XX部门兄弟反应, 其在将PHP从5.3.8 升级到5.5.13 时, 开始运行正常, 运行一段时间后, 系统负载变高,达 ...
- 前端之css样式01
选择器,css文本属性 CSS语法: 选择器 {属性1: 值1; 属性2: 值2} CSS放置的位置: 1. 直接写在标签里面,通过style属性来设置CSS样式 2. 在head标签里面通过styl ...
- WPF:XAML概述
简介 XAML是eXtensible Application Markup Language可扩展应用程序标记语言,它是微软公司为构建应用程序用户界面而创建的一种新的描述性语言.XAML提供了一种便于 ...
- [BZOJ5133][CodePlus2017年12月]白金元首与独舞
bzoj luogu 题意 给你一个\(n*m\)的网格,每个位置上有一个箭头指向上或下或左或右.有些位置上还没有箭头,现在要求你在这些没有箭头的位置上填入箭头,使得从网格的任意一个位置开始,都可以沿 ...