8.1.8 封装 jQuery 插件

上面几节就 jQuery 插件的创建方法进行了详细讲解,一般对外发布的自定义插件都应该进行封装,封装的插件还应该符合规范,只有这样所创建的插件才具有推广价值,并得到其他用户的喜爱。

封装 jQuery 插件的第一步是定义一个独立域,代码如下所示。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     // 自定义插件代码  
  4. })(jQuery); // 封装插件  
  5. </script>  

确定创建插件类型,选择创建方式。例如,创建一个设置元素字体颜色的插件,则应该创建 jQuery 对象方法。考虑到 jQuery 提供了插件扩展方法 extend() ,调用该方法定义插件会更为规范。代码如下。

[html] view plaincopy
  1. (function($){  
  2.     $.extend($.fn, {    // jQuery 对象方法扩展  
  3.         // 函数列表  
  4.     });  
  5. })(jQuery); // 封装插件  
一般插件都会接受参数,用来控制插件的行为,根据 jQuery 设计习惯,我们可以把所有参数以列表形式封装在选项对象中进行传递。例如,对于设置元素字体颜色的插件,应该允许用户设置字体颜色,同时还应该考虑如果用户没有设置颜色,则应确保使用默认色进行设置。实现代码如下所示。

[html] view plaincopy
  1. (function($){  
  2.     $.extend($.fn, {    // jQuery 对象方法扩展  
  3.         color: function(options){   // 自定义插件名称  
  4.             var options = $.extend({    // 参数选项对象处理  
  5.                 bcolor: "white",    // 背景色默认值  
  6.                 fcolor: "black"     // 前景色默认值  
  7.             }, options);  
  8.             // 函数体  
  9.         }  
  10.     });  
  11. })(jQuery); // 封装插件  
最后,设计插件自定义功能代码,如下所示。

[html] view plaincopy
  1. (function($){  
  2.     $.extend($.fn, {    // jQuery 对象方法扩展  
  3.         color: function(options){   // 自定义插件名称  
  4.             var options = $.extend({    // 参数选项对象处理  
  5.                 bcolor: "white",    // 背景色默认值  
  6.                 fcolor: "black"     // 前景色默认值  
  7.             }, options);  
  8.             return this.each(function(){    // 返回匹配的 jQuery 对象  
  9.                 $(this).css("color", options.fcolor);   // 遍历设置每个 DOM 元素的字体颜色  
  10.                 $(this).css("backgroundColor", options.bcolor); // 遍历设置每个 DOM 元素的背景色  
  11.             });  
  12.         }  
  13.     });  
  14. })(jQuery); // 封装插件  
完成插件封装之后,我们不妨来测试一下自定义的 color() 方法。代码如下。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         color: function(options){   // 自定义插件名称  
  5.             var options = $.extend({ // 参数选项对象处理      
  6.                 bcolor: "white",     // 背景色默认值  
  7.                 fcolor: "black"      // 前景色默认值  
  8.             }, options);  
  9.             return this.each(function(){ // 返回匹配的 jQuery 对象  
  10.                 $(this).css("color", options.fcolor);       // 遍历设置每个 DOM 元素的字体颜色  
  11.                 $(this).css("backgroundColor", options.bcolor);     // 遍历设置每个 DOM 元素的背景颜色  
  12.             });       
  13.         }  
  14.     });  
  15. })(jQuery); // 封装插件  
  16.   
  17. $(function(){   // 页面初始化  
  18.     $("h1").color({ // 设置标题的前景色和背景色  
  19.         bcolor: "#eea",  
  20.         fcolor: "red"  
  21.     });  
  22. });  
  23. </script>  
  24. </head>  
  25. <body>  
  26. <h1>标题文本</h1>  

8.1.9 优化 jQuery 插件 -- 开放公共参数

优秀的 jQuery 插件,应该以开放性的姿态满足不同个性化的设计要求,同时还应该做好封闭性,避免外界有意或无意的破坏。

首先,可以考虑开发插件的默认设置,这对于插件使用者来说,会更容易使用较少的代码覆盖和修改插件。

继续以上面的代码为例进行说明,把其中的参数默认值作为 $.fn.color 对象的属性单独进行设计,然后借助 jQuery.extend() 方法覆盖原来参数选项即可

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         color: function(options){     
  5.             var options = $.extend({}, $.fn.color.defaults, options);  
  6.             return this.each(function(){ // 返回匹配的 jQuery 对象  
  7.                 $(this).css("color", options.fcolor);       // 遍历设置每个 DOM 元素的字体颜色  
  8.                 $(this).css("backgroundColor", options.bcolor);     // 遍历设置每个 DOM 元素的背景颜色  
  9.             });       
  10.         }  
  11.     });  
  12.     $.fn.color.defaults = { // 独立设置 $.fn.color 对象的默认参数值  
  13.         bcolor: "white",  
  14.         fcolor: "black"  
  15.     };  
  16. })(jQuery); // 封装插件  
  17.   
  18. $(function(){   // 页面初始化  
  19.     $("h1").color({ // 设置标题的前景色和背景色  
  20.         bcolor: "#eea",  
  21.         fcolor: "red"  
  22.     });  
  23. });  
  24. </script>  
在 color() 函数中,jQuery.extend() 方法能够使用参数 options 覆盖默认的 defaults 属性值,如果没有设置 options 参数值,则使用 defaults 属性值。同时,由于 defaults 属性是单独定义的,故我们可以在页面中预设前景色和背景色,然后就可以多次调用 color() 方法,示例代码如下。通过这种开发插件默认参数的做法,用户不再需要重复定义参数,这样就可以节省开发时间。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         color: function(options){     
  5.             var options = $.extend({}, $.fn.color.defaults, options);  
  6.             return this.each(function(){ // 返回匹配的 jQuery 对象  
  7.                 $(this).css("color", options.fcolor);       // 遍历设置每个 DOM 元素的字体颜色  
  8.                 $(this).css("backgroundColor", options.bcolor);     // 遍历设置每个 DOM 元素的背景颜色  
  9.             });       
  10.         }  
  11.     });  
  12.     $.fn.color.defaults = { // 独立设置 $.fn.color 对象的默认参数值  
  13.         bcolor: "white",  
  14.         fcolor: "black"  
  15.     };  
  16. })(jQuery); // 封装插件  
  17.   
  18. $(function(){   // 页面初始化  
  19.     $.fn.color.defaults = { // 预设默认的前景色和背景色  
  20.         bcolor: "#eea",  
  21.         fcolor: "red"  
  22.     };  
  23.     $("h1").color();  
  24.     $("p").color({bcolor: "#fff"}); // 为段落文本设置默认色,同时覆盖背景色为白色  
  25.     $("div").color(); // 为盒子设置默认色  
  26. });  
  27. </script>  
  28. </head>  
  29. <body>  
  30. <h1>标题文本</h1>  
  31. <p>段落文本</p>  
  32. <div>盒子</div>  
  33. </body>  

8.1.10 优化jQuery插件 -- 开放部分功能

用过 Cycle 插件插件的读者可能会知道,它是一个滑动显示插件,支持很多内部变换功能,如滚动、滑动和渐变消失等。实际上,在封装插件时,我们无法把所有功能都封装进去,也没有办法定义滑动变化上每一种类型的变化效果。但是 Cycle 插件通过开放部分功能,允许用户重写 transitions 对象,这样就可以添加自定义变化效果,从而使该插件满足不同用户的不同需求。

Cycle 插件是这样开放部分功能的,代码如下。

[html] view plaincopy
  1. $.fn.cycle.transitions = {  
  2.     // 扩展方法  
  3. };  
这个技巧就可以允许其他用户定义和传递参数到 Cycle 插件内部。

例如,继续以上一节的示例为基础,我们为其添加一个格式化的扩展功能,这样用户在设置颜色的同时,还可以根据需要适当进行格式化功能设计,如加粗、斜体、放大等功能操作。扩展的 color() 插件代码如下所示。

[html] view plaincopy
  1. (function($){  
  2.     $.extend($.fn, {  
  3.         color: function(options){     
  4.             var options = $.extend({}, $.fn.color.defaults, options);  // 覆盖原来参数  
  5.             return this.each(function(){   
  6.                 $(this).css("color", options.fcolor);         
  7.                 $(this).css("backgroundColor", options.bcolor);       
  8.                 var _html = $(this).html();      // 获取当前元素包含的 HTML 字符串  
  9.                 _html = $.fn.color.format(_html); // 调用格式化功能函数对其进行格式化  
  10.                 $(this).html(_html); // 使用格式化的 HTML 字符串重写当前元素内容  
  11.             });       
  12.         }  
  13.     });  
  14.     $.fn.color.defaults = { // 独立设置 $.fn.color 对象的默认参数值  
  15.         bcolor: "white",  
  16.         fcolor: "black"  
  17.     };  
  18.     $.fn.color.format = function(str){  // 开放的功能函数  
  19.         return str;  
  20.     }  
  21. })(jQuery); // 封装插件  
在上面的示例中,通过开发的方式定义了一个 format() 功能函数,在这个功能函数中默认没有进行格式化设置,然后在 color() 函数体内利用这个开放性功能函数格式化当前元素内的 HTML 字符串。

例如,下面的示例调用了 color() 插件,同时在调用时分别扩展了它的格式化功能。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         color: function(options){     
  5.             var options = $.extend({}, $.fn.color.defaults, options);  // 覆盖原来参数  
  6.             return this.each(function(){   
  7.                 $(this).css("color", options.fcolor);         
  8.                 $(this).css("backgroundColor", options.bcolor);       
  9.                 var _html = $(this).html();      // 获取当前元素包含的 HTML 字符串  
  10.                 _html = $.fn.color.format(_html); // 调用格式化功能函数对其进行格式化  
  11.                 $(this).html(_html); // 使用格式化的 HTML 字符串重写当前元素内容  
  12.             });       
  13.         }  
  14.     });  
  15.     $.fn.color.defaults = { // 独立设置 $.fn.color 对象的默认参数值  
  16.         bcolor: "white",  
  17.         fcolor: "black"  
  18.     };  
  19.     $.fn.color.format = function(str){  // 开放的功能函数  
  20.         return str;  
  21.     }  
  22. })(jQuery); // 封装插件  
  23.   
  24. $(function(){   // 页面初始化  
  25.     $.fn.color.defaults = { // 预设默认的前景色和背景色  
  26.         bcolor: "#eea",  
  27.         fcolor: "red"  
  28.     };  
  29.     $.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本加粗显示  
  30.         return "<strong>" + str + "</strong>";  
  31.     };  
  32.     $("h1").color();  
  33.     $("p").color({bcolor: "#fff"}); // 为段落文本设置默认色,同时覆盖背景色为白色  
  34.     $.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本放大显示  
  35.         return "<span style='font-size: 30px;'>" + str + "</span>";  
  36.     };  
  37.     $("div").color(); // 为盒子设置默认色  
  38. });  
  39. </script>  
  40. </head>  
  41. <body>  
  42. <h1>标题文本</h1>  
  43. <p>段落文本</p>  
  44. <div>盒子</div>  
  45. </body>  
上述技巧让用户能够传递自己的功能设置,以覆盖插件默认的功能,从而方便了其他用户以当前插件为基础进一步去扩写插件。

8.1.11 优化 jQuery 插件 -- 保留插件隐私

优秀的插件,不仅仅要追求开放性,还应该留意插件的隐私性,对于不该暴露的部分,如果不注意保护,很容易被外界入侵,从而破坏插件的功能。因此,在设计插件时必须考虑插件实现中不应该暴露的部分。一旦被暴露,就需要铭记任何对于参数或者语义的改动也许会破坏向后的兼容性。如果不能确定不应该暴露的特定函数,那么就必须考虑如何进行保护的问题。

若插件包含很多函数,在设计时我们希望这么多函数不搅乱命名空间,也不会被完全暴露,惟一的方法就是使用闭包。为了创建闭包,可以将整个插件封装在一个函数中。

继续以上节示例进行讲解,为了验证用户在调用 color() 方法时所传递的参数是否合法,我们不妨在插件中定义一个参数验证函数,但是该验证函数是不允许外界侵入或者访问的,此时我们可以借助闭包把它隐藏起来,只允许在插件内部进行访问。实现的代码如下。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         color: function(options){     
  5.             if(!filter(options))    // 调用隐私方法验证参数,不合法则返回  
  6.                 return this;  
  7.             var options = $.extend({}, $.fn.color.defaults, options);  // 覆盖原来参数  
  8.             return this.each(function(){   
  9.                 $(this).css("color", options.fcolor);         
  10.                 $(this).css("backgroundColor", options.bcolor);       
  11.                 var _html = $(this).html();      // 获取当前元素包含的 HTML 字符串  
  12.                 _html = $.fn.color.format(_html); // 调用格式化功能函数对其进行格式化  
  13.                 $(this).html(_html); // 使用格式化的 HTML 字符串重写当前元素内容  
  14.             });       
  15.         }  
  16.     });  
  17.     $.fn.color.defaults = { // 独立设置 $.fn.color 对象的默认参数值  
  18.         bcolor: "white",  
  19.         fcolor: "black"  
  20.     };  
  21.     $.fn.color.format = function(str){  // 开放的功能函数  
  22.         return str;  
  23.     }  
  24.     function filter(options){ // 定义隐私函数,外界无法访问  
  25.         // 如果参数不存在,或者存在且为对象,则返回 true ,否则返回 false  
  26.         return !options || (options && typeof options === "object")?true: false;  
  27.     }  
  28. })(jQuery); // 封装插件  
  29.   
  30. $(function(){   // 页面初始化  
  31.     $.fn.color.defaults = { // 预设默认的前景色和背景色  
  32.         bcolor: "#eea",  
  33.         fcolor: "red"  
  34.     };  
  35.     $.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本加粗显示  
  36.         return "<strong>" + str + "</strong>";  
  37.     };  
  38.     $("h1").color();  
  39.     $("p").color({bcolor: "#fff"}); // 为段落文本设置默认色,同时覆盖背景色为白色  
  40.     $.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本放大显示  
  41.         return "<span style='font-size: 30px;'>" + str + "</span>";  
  42.     };  
  43.     $("div").color(); // 为盒子设置默认色  
  44. });  
  45. </script>  
  46. </head>  
  47. <body>  
  48. <h1>标题文本</h1>  
  49. <p>段落文本</p>  
  50. <div>盒子</div>  
  51. </body>  
这样对于下面的非法参数设置,则会忽略该方法的调用,但是不会抛出异常。

[html] view plaincopy
  1. $(function(){  
  2.     $("p").color("#fff");  
  3. });  

8.1.12 优化 jQuery 插件 -- 非破坏性操作

在特定情况下,jQuery 对象方法可能会修改 jQuery 对象匹配的 DOM 元素,这时就有可能破坏方法返回值的一致性。为了遵循 jQuery 框架的核心设计理念,我们应该时刻警惕任何修改 jQuery 对象的操作。

例如,定义一个 jQuery 对象方法 parent() ,用来获取 jQuery 匹配的所有 DOM 元素的父元素。实现代码如下。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         parent: function(){ // 扩展 jQuery 对象方法,获取所有匹配元素的父元素  
  5.             var arr = [];  
  6.             $.each(this, function(index, value){ // 遍历匹配的 DOM 元素  
  7.                 arr.push(value.parentNode); // 把匹配元素的父元素推入临时数组  
  8.             });  
  9.             arr = $.unique(arr);    // 在临时数组中过滤重复的元素  
  10.             return this.setArray(arr); // 把变量 arr 打包为数组类型返回  
  11.         }  
  12.     });  
  13. })(jQuery); // 封装插件  
  14. </script>  
在上面的 jQuery 对象方法中,通过遍历所有的匹配元素,获取每个 DOM 元素的父元素,并把这些父元素存储到一个临时数组中,通过过滤、打包再返回。

下面我们就用这个新方法为所有 p 元素的父元素添加一个边框,示例代码如下所示。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         parent: function(){ // 扩展 jQuery 对象方法,获取所有匹配元素的父元素  
  5.             var arr = [];  
  6.             $.each(this, function(index, value){ // 遍历匹配的 DOM 元素  
  7.                 arr.push(value.parentNode); // 把匹配元素的父元素推入临时数组  
  8.             });  
  9.             arr = $.unique(arr);    // 在临时数组中过滤重复的元素  
  10.             return this.setArray(arr); // 把变量 arr 打包为数组类型返回  
  11.         }  
  12.     });  
  13. })(jQuery); // 封装插件  
  14.   
  15. $(function(){  
  16.     var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中  
  17.     $p.parent().css("border", "solid 1px red");  // 调用 parent() 方法获取 p 元素的父元素,并设置它们的边框样式  
  18. });  
  19. </script>  
  20. </head>  
  21. <body>  
  22. <div style="width: 400px; height: 200px;">大盒子  
  23.     <p>段落文本1</p>  
  24.     <div style="width: 200px; height: 100px;">小盒子  
  25.         <p>段落文本2</p>  
  26.     </div>  
  27. </div>  
  28. </body>  
如果在设置了父元素的边框后,我们希望把 jQuery 对象匹配的所有元素都隐藏起来,则可以添加下面的代码,则在浏览器中预览就会发现 div 元素也被隐藏起来了。

[html] view plaincopy
  1. $(function(){  
  2.     var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中  
  3.     $p.parent().css("border", "solid 1px red");  // 调用 parent() 方法获取 p 元素的父元素,并设置它们的边框样式  
  4.     $p.hide();   // 隐藏所有 p 元素,即当前 jQuery 对象  
  5. });  
也就是说,在上面代码中 $p 变量已经被修改,它不再指向当前 jQuery 对象,而是指向 jQuery 对象匹配元素的父元素,因此在为 $p 调用 hide() 方法时,就会隐藏 div 元素,而不是 p 元素。

上面示例仅仅是破坏性操作的一种表现,如果要避免此类隐性修改 jQuery 对象的行为,建议采用非破坏性操作。例如,在本例中我们可以使用 pushStack() 方法创建一个新的 jQuery 对象,而不是修改 this 所引用的 jQuery 对象,这样可以避免破坏性操作行为,同时 pushStack() 方法还允许调用 end() 方法操作新创建的 jQuery 对象方法。把上面的示例的 jQuery 对象方法进行优化,代码如下所示。

[html] view plaincopy
  1. <script type="text/javascript">  
  2. (function($){  
  3.     $.extend($.fn, {  
  4.         parent: function(options){ // 扩展 jQuery 对象方法,获取所有匹配元素的父元素  
  5.             var arr = [];  
  6.             $.each(this, function(index, value){ // 遍历匹配的 DOM 元素  
  7.                 arr.push(value.parentNode); // 把匹配元素的父元素推入临时数组  
  8.             });  
  9.             arr = $.unique(arr);    // 在临时数组中过滤重复的元素  
  10.             return this.pushStack(arr); // 返回新创建的 jQuery 对象,而不是修改后的当前 jQuery 对象  
  11.         }  
  12.     });  
  13. })(jQuery); // 封装插件  
  14.   
  15. $(function(){  
  16.     var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中  
  17.     $p.parent().css("border", "solid 1px red");  // 调用 parent() 方法获取 p 元素的父元素,并设置它们的边框样式  
  18.     $p.hide();   // 隐藏所有 p 元素,即当前 jQuery 对象  
  19. });  
  20. </script>  
  21. </head>  
  22. <body>  
  23. <div style="width: 400px; height: 200px;">大盒子  
  24.     <p>段落文本1</p>  
  25.     <div style="width: 200px; height: 100px;">小盒子  
  26.         <p>段落文本2</p>  
  27.     </div>  
  28. </div>  
  29. </body>  
这时,如果继续执行上面的演示实例操作,则可以看到 div 元素边框样式被定义为红色实现了,同时也隐藏了其包含的 p 元素。

针对上面的代码,我们就可以采用连续行为进行编写了,代码如下所示。

[html] view plaincopy
  1. $(function(){  
  2.     var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中  
  3.     $p.parent().css("border", "solid 1px red").end().hide();  
  4. });  
其中 end() 方法能够恢复被破坏的 jQuery 对象,也就是说 parent() 方法返回的是当前元素的父元素的集合,现在调用 end() 方法之后,又恢复到最初的当前元素集合,此时可以继续调用方法作用于原来的 jQuery 对象上了。

本文转载:CSDN博客