jQuery 的流行在很大程度上归功于其对插件的支持。插件也就是功能扩展的意思,jQuery 允许任何开发人员超越最初的库函数创建并扩展 jQuery 函数。这种开放性框架设计思路催生了无数实用型的插件,jQuery 几乎能够提供 Web 应用程序内所需的任何一种函数。

jQuery 的易扩展性吸引了越来越多的开发者和业余爱好者去研究、设计和使用 jQuery 插件。目前,全球有超过上千种不同应用需要的插件。使用这些插件能够帮助开发人员提升开发速度,节约劳动成本。最权威的插件可以访问 jQuery 官方网站 (http://plugins.jquery.com) 获取。本章将重点讲解如何创建自己的 jQuery 插件,并就网络上比较经典的几个插件设计原理和使用进行介绍。

8.1 创建 jQuery 插件

网络上流传着成百上千的第三方插件,这些插件虽然能够增强我们的编程体验,但是很难满足所有设计需要,特别是个性化开发需求。如果自己编写的代码可以重用,或者供其他用户参考,很自然任何人都希望把这些代码进行封装,打包为一个新插件。这个实现过程并不困难,只要读者认真阅读本节内容即可轻松实现。

8.1.1 jQuery 插件的类型

jQuery 插件主要分为三种类型,说明如下。

1. jQuery 方法

这种类型的插件是把一些常用或者重复使用的功能定义为函数,然后绑定到 jQuery 对象上,从而成为 jQuery 对象的一个扩展方法。

目前,大部分jQuery插件都是这种类型的插件,由于这种插件是将对象方法封装起来,在 jQuery 选择器获取 jQuery 对象的过程中进行操作,从而发挥 jQuery 强大的选择器优势。有很多 jQuery 内部方法,也是在 jQuery 脚本内部通过这种形式插入到 jQuery 框架中的,如 parent() 、appendTo() 和 addClass() 等方法。

2.全局函数

也可以把自定义的功能函数独立附加到 jQuery 命名空间下,从而作为 jQuery 作用域下的一个公共函数使用。例如,jQuery 的 ajax() 方法就是利用这种途径内部定义的全局函数。

==由于全局函数没有被绑定到 jQuery 对象上,故不能够在选择器获取的 jQuery 对象上调用。需要通过 jQuery.fn() 或者 $.fn() 方式进行引用。==

3.选择器

jQuery 提供了强大的选择器,当然在个性化开发中,读者也可能会感觉到这些选择器不够用,或者使用不是很方便。这个时候,我们就可以考虑自定义选择器,以满足特定环境下的选择元素需要。

8.1.2 解析 jQuery 插件机制

为了方便用户创建插件,jQuery 自定义了 jQuery.extend() 和 jQuery.fn.extend() 方法。其中 jQuery.extend() 方法能够创建全局函数或者选择器,而 jQuery.fn.extend() 方法能够创建 jQuery 对象方法。

例如,下面的示例将在 jQuery 命名空间上创建两个公共函数。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.extend({ // 扩展 jQuery 的公共函数  
  3.     minValue: function(a, b){ // 比较两个参数值,返回最小值  
  4.         return a<b?a:b;  
  5.     },  
  6.     maxValue: function(a, b){ // 比较两个参数值,返回最大值  
  7.         return a<b?b:a;  
  8.     }  
  9. });  
  10. </script>  
然后就可以在页面中调用这两个公共函数,示例代码如下所示。在下面这个示例中,当单击按钮后,浏览器会弹出提示对话框,要求输入两个值,然后提示两个值的大小。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.extend({ // 扩展 jQuery 的公共函数  
  3.     minValue: function(a, b){ // 比较两个参数值,返回最小值  
  4.         return a<b?a:b;  
  5.     },  
  6.     maxValue: function(a, b){ // 比较两个参数值,返回最大值  
  7.         return a<b?b:a;  
  8.     }  
  9. });  
  10.   
  11. $(function(){  
  12.     $("input").click(function(){  
  13.         var a = prompt("请输入一个数值?");  
  14.         var b = prompt("请再输入一个数值?");  
  15.         var c = jQuery.minValue(a, b);  
  16.         var d = jQuery.maxValue(a, b);  
  17.           
  18.         alert("你输入的最大值是:" + d + "\n你输入的最小值是:" + c);  
  19.     });  
  20. });  
  21. </script>  
  22. </head>  
  23. <body>  
  24. <input type="button" value="jQuery 插件扩展测试" />  
  25. </body>  

jQuery.extend() 和 jQuery.fn.extend() 方法都包含一个参数,该参数仅接受名/值对结构的对象,其中名表示函数或方法名,而值表示函数体。关于如何创建 jQuery 插件的问题,我们将在下面小节中进行详细讲解。

jQuery.extend() 方法除了可以创建插件外,还可以用来扩展 jQuery 对象。例如,在下面的示例中,调用 jQuery.extend() 方法把对象 a 和 b 合并为一个新的对象,并返回合并对象将其赋值给变量 c 。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. var a = { // 对象直接量  
  3.     name: "zhu",  
  4.     pass: 123  
  5. };  
  6. var b = { // 对象直接量  
  7.     name: "wang",  
  8.     pass: 456,  
  9.     age: 1  
  10. };  
  11. var c = jQuery.extend(a, b);  // 合并对象a 和 b  
  12.   
  13. $(function(){  
  14.     for(var name in c){ // 遍历对象c,显示合并后的对象c的具体属性和值  
  15.         $("div").html($("div").html() + "<br/>" + name + ":" + c[name]);  
  16.     }  
  17. });  
  18. </script>  
  19. </head>  
  20. <body>  
  21. <div></div>  
  22. </body>  

在实际开发中,常常使用 jQuery.extend() 方法为插件方法传递系列选项结构的参数。

这样当在调用该方法时,如果想传递新的参数值,就会覆盖掉默认的参数选项值,或者向函数参数添加新的属性和值;如果没有传递参数,则保持并使用默认值。例如,在下面几个函数调用中,分别传入新值,或者添加新参数,或者保持默认值。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. function fn(options){  
  3.     var options = jQuery.extend({ // 默认参数选项列表  
  4.         name1: value1,  
  5.         name2: value2,  
  6.         name3: value3,  
  7.     }, options); // 使用函数的参数覆盖或合并到默认参数选项列表中  
  8.     // 函数体  
  9. }  
  10.   
  11. fn({name1: value2,name2: value3, name3: value1});  // 覆盖新值  
  12. fn({name4: value4,name5: value5}); // 添加新选项  
  13. fn(); // 保持默认参数值  
  14. </script>  

jQuery.extend() 方法的对象合并机制,比传统的逐个检测参数不仅灵活且简洁,使用命名参数添加新选项也不会影响已编写的代码风格,让代码变得更加直观明白。

8.1.3 创建 jQuery 全局函数

jQuery 内置的很多方法都是通过全局函数实现的。所谓全局函数,就是 jQuery 对象的方法,实际上就是位于 jQuery 命名空间内部的函数。

有人把这类函数称为实用工具函数,这些函数有一个共同特征,就是不直接操作 DOM 元素,而是操作 JavaScript 的非元素对象,或者执行其他非对象的特定操作,如 jQuery 的 each() 函数和 noConflict() 函数。

回忆一下上一章讲解过的 ajax() 方法,它就是一个典型的 jQuery 全局函数,$.ajax() 所做的一切都可以通过调用名称为 ajax() 的全局函数来实现。但是,这种方式会带来函数冲突问题,如果把函数放置在 jQuery 命名空间内,就会降低这种冲突,只要在 jQuery 命名空间内注意不要与 jQuery 其他方法冲突即可。

在上一节中曾经介绍了使用 jQuery.extend() 方法可以扩展 jQuery 对象的全局函数。当然,我们也可以使用下面的方法快速定义 jQuery 全局函数。例如,针对上一节示例,我们也可以按如下方法进行编写。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.minValue = function(a,b){  
  3.     return a<b?a:b;  
  4. };  
  5. jQuery.maxValue = function(a,b){  
  6.     return a<b?b:a;  
  7. };  
  8. </script>  
也就是说,如果要向 jQuery 命名空间上添加一个函数,只需要将这个新函数指定为 jQuery 对象的一个属性即可。其中 jQuery 对象名也可以简写为 $ 。

考虑到 jQuery 的插件越来越多,因此在使用时可能会遇到自己的插件与第三方插件名发生冲突的问题。为了避免这个问题,建议把属于自己的插件都封装在一个对象中。例如,针对上面两个创建的全局函数,可以把它们封装在自己的对象中,实现代码如下。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.css8 = {  
  3.     minValue: function(a,b){  
  4.         return a<b?a:b;  
  5.     },  
  6.     maxValue: function(a,b){  
  7.         return a<b?b:b;  
  8.     }  
  9. };  
  10. </script>  
尽管我们仍然可以把这些函数当成全局函数来看待,但是从技术层面分析,它们现在都是 jQuery 全局函数的方法,因此在调用这些函数时,请求方式也会发生变化,调用函数的代码如下。

[html] view plaincopy
  1. var c = jQuery.css8.minValue(a,b);  
  2. var d = jQuery.css8.maxValue(a,b);  
这样就可以轻松避免与别的插件发生冲突。注意,即使页面中包含了 jQuery 框架文件,但是考虑到安全性,不建议以一种简写的方式 (即使用 $ 代替 jQuery) 进行书写,我们应该在编写插件中始终使用 jQuery 来调用 jQuery 方法。

8.1.4 使用 jQuery.fn 对象创建 jQuery 对象方法

除了全局函数外,jQuery 中的大多数功能都是通过 jQuery 对象的方法提供的,这些对象方法对于 DOM 操作来说非常方便,这也是很多用户喜欢并选用 jQuery 框架的原因。

创建全局函数只需要通过为 jQuery 对象添加属性即可,而创建 jQuery 对象方法也可以通过为 jQuery.fn 对象添加属性来实现。实际上 jQuery.fn 对象就是 jQuery.prototype 原型对象的别名,使用别名更方便引用。例如,下面这个函数就是一个简单的 jQuery 对象方法,当调用该方法时,将会弹出一个提示对话框。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.fn.test = function(){  
  3.     alert('这是jQuery对象方法!');  
  4. };  
  5. </script>  
然后,就可以在任何 jQuery 对象中调用该方法。在下面的应用示例中,如果单击页面中的 "jQuery插件扩展测试" 按钮,即可弹出一个提示对话框,提示 "这是jQuery对象方法!"。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.fn.test = function(){  
  3.     alert('这是jQuery对象方法!');  
  4. };  
  5. $(function(){  
  6.     $("input").click(function(){ // 绑定 click 事件  
  7.         $(this).test(); // 在当前的 jQuery 对象上调用 test() 方法  
  8.     });  
  9. });  
  10. </script>  
  11. </head>  
  12. <body>  
  13. <input type="button" value="jQuery插件扩展测试" />  
  14. </body>  

由于这里并没有在任何地方匹配 DOM 节点,所以编写全局函数也是可以的。但是,在使用 jQuery 对象方法时,函数体内的 this 关键字总是引用当前 jQuery 对象,因此我们可以对上面的方法进行重写,实现动态提示信息。代码如下。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.fn.test = function(){  
  3.     alert(this[0].nodeName);    // 提示当前 jQuery 对象的 DOM 节点名称  
  4. };  
  5. $(function(){  
  6.     $("input").click(function(){    // 绑定 click 事件  
  7.         $(this).test(); // 在当前的 jQuery 对象上调用 test() 方法  
  8.     });  
  9. });  
  10. </script>  
  11. </head>  
  12. <body>  
  13. <input type="button" value="jQuery插件扩展测试" />  
  14. </body>  

这样,当单击按钮时,就会弹出当前元素的节点名称。

在上面的示例中,可以看到由于 jQuery 选择器返回的是一个数组类型的 DOM 节点集合,this 指针就指向当前这个集合,故要显示当前元素的节点名称,就必须在 this 指针后面指定当前元素的序号。

试想,如果 jQuery 选择器匹配多个元素,我们该如何准确指定当前元素对象呢?

要解决这个问题,我们不妨在当前 jQuery 对象方法的环境中调用 each() 方法,通过隐式迭代的方式,让 this 指针依次引用每一个匹配的 DOM 元素对象。这样也能够保持插件与 jQuery 内置方法保持一致。例如,针对上面的示例做进一步的修改。

[html] view plaincopy
  1. jQuery.fn.test = function(){  
  2.     this.each(function(){   // 遍历所有匹配的元素,此处的 this 表示对象集合,即 jQuery 对象  
  3.         alert(this.nodeName); // 显示当前元素的节点名称,此处的 this 表示元素对象  
  4.     });  
  5. };  
然后,在调用该方法时,就不用担心 jQuery 选择器所匹配的元素有多少了。例如,在下面的示例中,当单击不同类型的元素,会提示当前元素的节点名称。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.fn.test = function(){  
  3.     this.each(function(){   // 遍历所有匹配的元素,此处的 this 表示对象集合,即 jQuery 对象  
  4.         alert(this.nodeName); // 显示当前元素的节点名称,此处的 this 表示元素对象  
  5.     });  
  6. };  
  7. $(function(){  
  8.     $("body *").click(function(){  // 选择 body 元素下所有元素  
  9.         $(this).test(); // 为当前元素调用 test()方法,提示当前 DOM 元素对象的节点名称  
  10.     });  
  11. });  
  12. </script>  
  13. </head>  
  14. <body>  
  15. <input type="button" value="jQuery插件扩展测试" />  
  16. <div>div元素</div>  
  17. <p>p元素</p>  
  18. <span>span元素</span>  
  19. </body>  
这样就可以实现根据前面选择器所匹配元素的不同,令所定义的 test() 方法给出不同的提示信息。

用惯 jQuery 的用户可能习惯于连写行为,也就是说在调用一个方法之后,紧跟着调用另一个方法,如此连写不断,形成一个珍珠链,而且编写灵活、方便,也符号人的思维习惯。例如

$(this).test().hide().height();

要实现类似的行为连写功能,就应该在每个插件方法中返回一个 jQuery 对象,除非方法需要明确返回值。返回的 jQuery 对象通常就是 this 所引用的对象。如果使用 each() 方法迭代 this ,则可以直接返回迭代的结果。例如,针对上面的示例做如下进一步的修改。

[html] view plaincopy
  1. jQuery.fn.test = function(){  
  2.     return this.each(function(){    // 返回迭代的 jQuery 对象  
  3.         alert(this.nodeName);   
  4.     });  
  5. };  
然后,我们就可以在应用示例中连写行为了。例如,在下面的示例中,先弹出提示节点名称的信息,然后使用当前节点名称改写当前元素内包含的信息,最后再慢慢隐藏该元素。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.fn.test = function(){  
  3.     return this.each(function(){    // 返回迭代的 jQuery 对象  
  4.         alert(this.nodeName);   
  5.     });  
  6. };  
  7. $(function(){  
  8.     $("body *").click(function(){  // 选择 body 元素下所有元素  
  9.         $(this).test().html(this.nodeName).hide(4000);  // 行为连写  
  10.     });  
  11. });  
  12. </script>  
  13. </head>  
  14. <body>  
  15. <input type="button" value="jQuery插件扩展测试" />  
  16. <div>div元素</div>  
  17. <p>p元素</p>  
  18. <span>span元素</span>  
  19. </body>  

8.1.5 使用 extend() 方法创建 jQuery 对象方法

jQuery.extend() 方法能够创建全局函数,而 jQuery.fn.extend() 方法可以创建 jQuery 对象方法。如果读者明白了上一节使用 jQuery.fn 对象属性的方法创建 jQuery 对象方法,那么使用 extend() 方法创建 jQuery 对象就比较容易理解了。

例如,针对上节中介绍的示例,我们可以调用 jQuery.fn.extend() 方法来创建 jQuery 对象方法。具体代码如下。

[html] view plaincopy
  1. jQuery.fn.extend({  
  2.     test: function(){  
  3.         return this.each(function(){  
  4.             alert(this.nodeName);   
  5.         });  
  6.     }  
  7. });  
jQuery.fn.extend()方法仅包含一个参数,该参数是一个对象直接量,是以名/值对形式组成的多个属性,名称表示方法名称,而值表示函数体。因此,在这个对象直接量中可以附加多个属性,为 jQuery 对象同时定义多个方法。

针对上面定义的 test() 方法,同样可以在 jQuery 选择器中直接调用。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. jQuery.fn.extend({  
  3.     test: function(){  
  4.         return this.each(function(){  
  5.             alert(this.nodeName);   
  6.         });  
  7.     }  
  8. });  
  9.   
  10. $(function(){  
  11.     $("body *").click(function(){  // 选择 body 元素下所有元素  
  12.         $(this).test().html(this.nodeName).hide(4000);  // 行为连写  
  13.     });  
  14. });  
  15. </script>  
  16. </head>  
  17. <body>  
  18. <input type="button" value="jQuery插件扩展测试" />  
  19. <div>div元素</div>  
  20. <p>p元素</p>  
  21. <span>span元素</span>  
  22. </body>  

本文转载:CSDN博客