首页 > 代码库 > 第七章(插件的使用和写法)(7.6 编写 jQuery 插件)
第七章(插件的使用和写法)(7.6 编写 jQuery 插件)
7.6.1 插件的种类
编写插件的目的是给已经有的一系列方法或函数做一个封装,以便在其他地方重复使用,方便后期维护和提高开发效率。
jQuery 的插件主要分为3种类型。
1 封装对象方法的插件
这种插件是将对象方法封装起来,用于对通过选择器获取的 jQuery 对象进行操作,是最常见的一种插件。
据不完全统计,95%以上的 jQuery 插件都是封装对象方法的插件,此类插件可以发挥出 jQuery 选择器的强大优势。有相当一部分的 jQuery 的方法,都是在 jQuery 脚本库内部通过这种形式,“插”在内核上,例如 parent() 方法,appendTo() 方法和 addClass() 方法等不少DOM操作方法。
不少用户对 jQuer 没有提供 color() 方法而表示遗憾,不得不使用 css("color")来代替,下面会编写一个 color() 方法的 jQuery 插件。
2 封装全局函数的插件
可以将独立的函数加到 jQuery 命名空间之下,例如第一章提到解决冲突用的 jQuery.noConflict() 方法、常用的 jQuery.ajax() 方法以及去除首位空格的 jQuery.trim() 方法等,都是 jQuery 内部作为全局函数的插件附加到内核上去的。
3 选择器插件
个别情况下,会需要用到选择器插件,虽然 jQuery 的选择器十分强大,但还是会需要扩充一些自己喜欢的选择器,例如用: color(red) 来选择所有红色字的元素之类的想法。
7.6.2 插件的基本要点
? jQuery 插件的文件名推荐命名为 jquery.[插件名].js,以免和其他 .js 库插件混淆,例如: jquery.color.js
? 所有的对象方法都应当附加到 jQuery.fn 对象上,而所有的全局函数都应当附加到 jQuery 对象本身上
? 在插件内部,this 指向的是当前通过选择器获取的 jQuery 对象,而不是一般的方法那样,例如 click() 方法,内部的this 指向的是 DOM 元素
? 可以通过 this.each 来遍历所有元素
? 所有的方法或函数插件,都应当以分号结尾,否则压缩的时候可能出现问题。为了更稳妥些,甚至可以在插件头部先加上一个分号,以免他人的不规范代码给插件带来影响
? 插件应该返回一个 jQuery 对象,以保证插件的可链式操作,除非插件需要返回的是一些需要获取的量,例如字符串或者数组等
? 避免在插件内部使用 $ 作为 jQuery 对象的别名,而应使用完整的 jQuery 来表示,这样可以避免冲突。当然,也可以利用闭包技巧来回避这个问题,使插件内部继续使用 $ 作为 jQuery 的别名。
7.6.3 插件中的闭包
闭包:允许使用内部函数(即函数定义和函数表达式位于另一个函数的函数体内),而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数,当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包
闭包含义:即内部函数会在外部函数返回后被执行,而当这个内部函数执行时,它仍然必须访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。
利用闭包特性,既可以避免内部临时变量影响全局空间,又可以在插件内部继续使用 $ 作为 jQuery 的别名,常见的 jQuery 插件都是以下形式:
(function(){ /*这里置放代码*/ })();
首先定义一个匿名函数 function(){ /*置放代码*/ },然后用括号括起来,变成( function(){ /*置放代码*/ }) 这种形式,最后通过 () 这个运算符来执行,可以传递参数进去,以供内部函数使用。
// 注意为了更好的兼容性,开始前有个分号 ; (function($){ //此处将 $ 作为匿名函数的形参 /*这里置放代码,可以使用 $ 作为 jQuery 的缩写别名*/ })(jQuery); // 这里就将 jQuery 作为实参传递给匿名函数了
上述代码是一种常见的 jQuery 插件结构。
代码 :
; (function($){ // 这里编写插件的代码,可以继续是哦那个 $ 作为 jQuery 的别名 // 定义一个局部变量 foo, 仅函数内部可以访问,外部无法访问 var foo; var bar = function(){ /* 在匿名函数内部的函数都可以访问foo,即便是匿名函数的外部调用 bar()的时候,也可以在 bar() 的内部访问到 foo,但在匿名函数的外部直接访问 foo是做不到的 */ } /* 下面的语句让匿名函数内部的函数 bar() 逃逸到全局可访问的范围内,这样就可以在匿名函数的外部通过调用 jQuery.BAR() 来访问内部定义的函数 bar(),并且内部函数 bar() 也能访问匿名函数内的变量 foo */ $.BAR = bar; })(jQuery);
7.6.4 jQuery 插件的机制
jQuery 提供了两个用于扩展 jQuery 功能的方法,即 jQuery.fn.extend() 方法和 jQuery.extend() 方法,前者用于扩展之前提到的3种类型插件中的第一种,后者用于扩展后两种插件。这两个方法都接受一个参数,类型为 Object,Object 对象的“名/值对”分别代表“函数或方法名/函数主体”
jQuery.extend() 方法除了可以用于扩展 jQuery 对象外,还有一个很强大的功能,就是用于扩展已有的 Object 对象。
jQuery 代码如下:
jQuery.extend(target,obj1,..........[objN]);
用一个或多个其他对象来扩展一个对象,然后返回被扩展的对象。
例如合并 settings 对象和 options 对象,修改并返回 settings 对象
var settings = {validate: false, limit:5, name:"foo"}; var options = {validate:true, name:"bar"}; var newOptions = jQuery.extend(settings, options); // 结果为: newOptions = {validate:true , limit: 5, name:"bar"};
jQuery.extend() 方法经常被用于设置插件方法的一系列默认参数,如下所示:
function foo(options){ options = jQuery.extend({ name: "bar", length: 5, dataType: "xml" /*默认参数*/ }, options ); /*options 为传递的参数*/ };
如果用户调用 fooff() 方法的时候,在传递的参数 options 对象中设置了相应的值,那么就使用设置的值,否则使用默认值。代码如下:
foo({ name:"a",length:"4",dataType:"json" }); foo({ name:"a",length:"4" }); foo({ name:"a" }); foo();
通过使用 jQuery.extend() 方法,可以很方便的用传入的参数来覆盖默认值,此时,对方法的调用依旧保持一致,只不过要传入的是一个映射而不是一个参数列表。
特点: 这种机制比传统的每个参数都去检测的方式不仅灵活而且更简洁,此外,使用命名参数意味着再添加新选项也不会影响过去编写的代码,使代码更加直观明了。
7.6.5 编写 jQuery 插件
1 封装 jQuery 对象方法的插件
? 编写设置和获取颜色的插件
首先编写一个 color() 插件,该插件用于实现以下两个功能:
① 设置匹配元素的颜色
② 获取匹配的元素(元素集合中的第 1 个)的颜色
首先将该插件按规范命名为 jquery.color.js
然后在 Javascript 文件里搭好框架,如下:
; (function($){ // 这里写插件代码 })(jQuery);
由于是对 jQuery 对象的方法扩展,因此采用扩展第 1 类插件的方法 jQuery.fn.extend() 来编写
; (function($){ $.fn.extend({ "color":function(value){ // 这里写插件代码 } }); })(jQuery);
这里给这个方法提供一个参数 value ,如果调用方法的时候传递了 valud 这个参数,那么就是用这个值来设置字体颜色,否则就是获取匹配元素的字体颜色的值。
首先实现第一个功能,设置字体颜色。
只需要简单地调用 jQuery 提供的 css() 方法,直接写成 this.css("color", value ) 即可。
注意:插件内部的 this 指向的是 jQuery 对象,而非普通的 DOM 对象,接下来要注意,插件如果不需要返回字符串之类的特定值,应当使其具有可链接性。为此,直接,直接返回这个 this 对象,由于 .css() 方法也会返回调用它的对象,即此处的 this , 因此可以将代码写成:
; (function($){ $.fn.extend({ "color":function(value){ return this.css("color",value); } }); })(jQuery);
接下来实现第二个功能,如果没有给方法传递参数,那么就是获取集合对象中第1个对象的 color 的值,由于 css() 方法本身就具有返回第 1 个匹配元素的样式值的功能,因此此处无需通过 eq() 来获取第1 个元素,只要将这两个功能结合起来,判断一下 value 是否是 undefined 即可。
jQuery 代码如下:
; (function($){ $.fn.extend({ "color":function(value){ if(value =http://www.mamicode.com/= undefined){ return this.css("color"); }else{ return this.css("color", value); } } }); })(jQuery);
此时,color() 插件的功能已经全部实现了。通过该插件可以获取和设置元素的 color 值。
实际上,css() 方法内部已经有判断 value 是否为 undefined 的机制,所以才可以根据传递参数的不同而返回不同的值。因此,可以借助 css() 方法的这个特性来处理该问题。 删除 if() 部分,最终剩余的代码实际上与先前那一段是一样的,jQuery 代码如下:
; (function($){ $.fn.extend({ "color":function(value){ return this.css("color"); } }); })(jQuery);
示例测试:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> .a{color: red;} </style> <script type="text/javascript" src="jquery.js"></script> </head> <body> <body> <div class="a">red</div> <div style="color:blue">blue</div> <div style="color:green">green</div> <div style="color:yellow">yellow</div> <script type="text/javascript"> // 插件编写 ;(function($){ $.fn.extend({ "color":function(value){ if(value == undefined){ return this.css("color"); }else{ return this.css("color",value); } } }); })(jQuery); // 插件应用 $(function(){ // 查看第一个 div 的color 样式值 alert($("div").color()+"\n 返回字符串,证明此插件可用。"); // 把所有的 div 字体颜色设为红色 alert($("div").color("red")+"\n 返回object 证明得到的是 jQuery对象") }) </script> </body> </html>
获取第一个元素的color值
设置元素的 color 属性为红色
如果需要定义一组插件,可以使用如下写法:
;(function($){ "color":function(value){ // 插件代码 }, "border":function(value){ // 插件代码 }, "background":function(value){ // 插件代码 } })(jQuery);
扩 展
随机更换字体颜色
// 插件编写 ;(function($){ $.fn.extend({ "color":function(value){ if(value =http://www.mamicode.com/= undefined){ return this.css("color"); }else{ return this.css("color",value); } } }); })(jQuery); // 插件应用 $(function(){ $("div").click(function(){ var color = $(this).text(); $("div").color(color); }) })
点击文字,随机更换颜色
? 表格隔行变色插件
jQuery 代码如下:
<script> $(function(){ $("tbody>tr:odd").addClass("odd"); $("tbody>tr:even").addClass("even"); $(‘tbody>tr‘).click(function() { //判断当前是否选中 var hasSelected=$(this).hasClass(‘selected‘); //如果选中,则移出selected类,否则就加上selected类 $(this)[hasSelected?"removeClass":"addClass"](‘selected‘) //查找内部的checkbox,设置对应的属性。 .find(‘:checkbox‘).attr(‘checked‘,!hasSelected); }); // 如果单选框默认情况下是选择的,则高色. $(‘tbody>tr:has(:checked)‘).addClass(‘selected‘); }) </script>
首先把插件取名为:alterBgColor,然后为该插件方法搭好框架,jQuery 代码如下:
// 插件编写 ;(function($){ $.fn.extend({ "alterBgColor":function(options){ // 插件代码 } }); })(jQuery);
框架完成后,接下来需要为 options 定义默认值,默认构成这样 ({odd:“odd”,even:"even",selected:"selected"}) 一个Object 。这样就可以通过 $("#sometable").alterBgColor({odd:“odd”,even:"even",selected:"selected"}) 自定义奇偶行的样式类名以及选中后的样式类名。同时,直接使用 $("#sometable").alterBgColor() 就可以应用默认的样式类名。
jQuery 代码如下:
; (function($){ $.fn.extend({ "alterBgColor":function(options){ options = $.extend({ odd:"odd", /*偶数行样式*/ even:"even", /*奇数行样式*/ selected:"selected" /*选中行样式*/ },options); } }); })(jQuery);
如果在后面的程序中需要使用 options 对象中的属性,可以使用如下方式来获得:
options.odd; //获取options 对象中的 odd属性的值 options.even; //获取options 对象中的 even属性的值 options.selected //获取options 对象中的 selected属性的值
接下来就需要把这些值放到程序中,来代替先前程序中的固定值。
最后就是匹配元素的问题了,显然不能直接用 $("tbody>tr")选择表格行,这样会使页面中全部的 <tr>元素都隔行变色,应该使用选择器选中某个表格,然后执行 alterBgColor() 方法后,将对应的表格内<tr>元素进行隔行变色。因此,需要把所有通过 $("tbody>tr")选择的对象改写成 $("tbody>tr", this),表示在匹配的元素内(当前表格内)查找,并应用上一步中的默认值。
jQuery 代码如下:
;(function($) { $.fn.extend({ "alterBgColor":function(options){ //设置默认值 options=$.extend({ odd:"odd", /* 偶数行样式*/ even:"even", /* 奇数行样式*/ selected:"selected" /* 选中行样式*/ },options); $("tbody>tr:odd",this).addClass(options.odd); $("tbody>tr:even",this).addClass(options.even); $(‘tbody>tr‘,this).click(function() { //判断当前是否选中 var hasSelected=$(this).hasClass(options.selected); //如果选中,则移出selected类,否则就加上selected类 $(this)[hasSelected?"removeClass":"addClass"](options.selected) //查找内部的checkbox,设置对应的属性。 .find(‘:checkbox‘).attr(‘checked‘,!hasSelected); }); // 如果单选框默认情况下是选择的,则高色. $(‘tbody>tr:has(:checked)‘,this).addClass(options.selected); return this; //返回this,使方法可链。 } }); })(jQuery);
在代码最后,返回this ,让这个插件具有可链性。
测试插件
构建2个表格
<title></title> <style type="text/css"> table { border:0;border-collapse:collapse;} td { font:normal 12px/17px Arial;padding:2px;width:100px;} th { font:bold 12px/17px Arial;text-align:left;padding:4px;border-bottom:1px solid #333;} .even { background:#FFF38F;} /* 偶数行样式*/ .odd { background:#FFFFEE;} /* 奇数行样式*/ .selected { background:#FF6500;color:#fff;} </style> </head> <body> <table id="table1"> <thead><tr><th> </th><th>姓名</th><th>性别</th><th>暂住地</th></tr></thead> <tbody> <tr> <td><input type="checkbox" name="choice" value=""/></td> <td>张山</td> <td>男</td> <td>浙江宁波</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" /></td> <td>李四</td> <td>女</td> <td>浙江杭州</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" checked="checked" /></td> <td>王五</td> <td>男</td> <td>湖南长沙</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" /></td> <td>找六</td> <td>男</td> <td>浙江温州</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" /></td> <td>Rain</td> <td>男</td> <td>浙江杭州</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" checked="checked" /></td> <td>MAXMAN</td> <td>女</td> <td>浙江杭州</td> </tr> </tbody> </table> <br /><br /> <table id="table2"> <thead><tr><th> </th><th>姓名</th><th>性别</th><th>暂住地</th></tr></thead> <tbody> <tr> <td><input type="checkbox" name="choice" value=""/></td> <td>张山</td> <td>男</td> <td>浙江宁波</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" /></td> <td>李四</td> <td>女</td> <td>浙江杭州</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" checked="checked" /></td> <td>王五</td> <td>男</td> <td>湖南长沙</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" /></td> <td>找六</td> <td>男</td> <td>浙江温州</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" /></td> <td>Rain</td> <td>男</td> <td>浙江杭州</td> </tr> <tr> <td><input type="checkbox" name="choice" value="" checked="checked" /></td> <td>MAXMAN</td> <td>女</td> <td>浙江杭州</td> </tr> </tbody> </table> </body>
应用插件
// 插件应用 $(function(){ $("#table2") .alterBgColor() //应用插件 .find("th").css("color","red"); // 可链式操作 })
注意:
jQuery 的选择符可能会匹配1个或多个元素,因此,在编写插件时必须考虑到这些情况,可以在插件内部调用 each() 方法来遍历匹配元素,然后执行相应的方法,this 会依次引用每个 DOM 元素
;(function($){ $.fn.extend({ "somePlugin":function(options){ return this.each(function(){ // 这里置放插件代码 }); } }); })(jQuery);
2 封装全局函数的插件
第七章(插件的使用和写法)(7.6 编写 jQuery 插件)