天天看点

第8章 高效开发和使用插件 (二)

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

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

<scripttype="text/javascript">

(function($){

//自定义插件代码

})(jQuery);//封装插件

</script>

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

$.extend($.fn,{//jQuery对象方法扩展

//函数列表

});

一般插件都会接受参数,用来控制插件的行为,根据 jQuery 设计习惯,我们可以把所有参数以列表形式封装在选项对象中进行传递。例如,对于设置元素字体颜色的插件,应该允许用户设置字体颜色,同时还应该考虑如果用户没有设置颜色,则应确保使用默认色进行设置。实现代码如下所示。

color:function(options){//自定义插件名称

varoptions=$.extend({//参数选项对象处理

bcolor:"white",//背景色默认值

fcolor:"black"//前景色默认值

},options);

//函数体

}

最后,设计插件自定义功能代码,如下所示。

returnthis.each(function(){//返回匹配的jQuery对象

$(this).css("color",options.fcolor);//遍历设置每个DOM元素的字体颜色

$(this).css("backgroundColor",options.bcolor);//遍历设置每个DOM元素的背景色

完成插件封装之后,我们不妨来测试一下自定义的 color() 方法。代码如下。

$.extend($.fn,{

$(this).css("backgroundColor",options.bcolor);//遍历设置每个DOM元素的背景颜色

$(function(){//页面初始化

$("h1").color({//设置标题的前景色和背景色

bcolor:"#eea",

fcolor:"red"

</head>

<body>

<h1>标题文本</h1>

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

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

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

color:function(options){

varoptions=$.extend({},$.fn.color.defaults,options);

$.fn.color.defaults={//独立设置$.fn.color对象的默认参数值

bcolor:"white",

fcolor:"black"

};

在 color() 函数中,jQuery.extend() 方法能够使用参数 options 覆盖默认的 defaults 属性值,如果没有设置 options 参数值,则使用 defaults 属性值。同时,由于 defaults 属性是单独定义的,故我们可以在页面中预设前景色和背景色,然后就可以多次调用 color() 方法,示例代码如下。通过这种开发插件默认参数的做法,用户不再需要重复定义参数,这样就可以节省开发时间。

$.fn.color.defaults={//预设默认的前景色和背景色

$("h1").color();

$("p").color({bcolor:"#fff"});//为段落文本设置默认色,同时覆盖背景色为白色

$("div").color();//为盒子设置默认色

<p>段落文本</p>

<div>盒子</div>

</body>

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

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

$.fn.cycle.transitions={

//扩展方法

这个技巧就可以允许其他用户定义和传递参数到 Cycle 插件内部。

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

varoptions=$.extend({},$.fn.color.defaults,options);//覆盖原来参数

returnthis.each(function(){

$(this).css("color",options.fcolor);

$(this).css("backgroundColor",options.bcolor);

var_html=$(this).html();//获取当前元素包含的HTML字符串

_html=$.fn.color.format(_html);//调用格式化功能函数对其进行格式化

$(this).html(_html);//使用格式化的HTML字符串重写当前元素内容

$.fn.color.format=function(str){//开放的功能函数

returnstr;

在上面的示例中,通过开发的方式定义了一个 format() 功能函数,在这个功能函数中默认没有进行格式化设置,然后在 color() 函数体内利用这个开放性功能函数格式化当前元素内的 HTML 字符串。

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

$.fn.color.format=function(str){//扩展color()插件的功能,使内部文本加粗显示

return"<strong>"+str+"</strong>";

$.fn.color.format=function(str){//扩展color()插件的功能,使内部文本放大显示

return"<spanstyle='font-size:30px;'>"+str+"</span>";

上述技巧让用户能够传递自己的功能设置,以覆盖插件默认的功能,从而方便了其他用户以当前插件为基础进一步去扩写插件。

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

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

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

if(!filter(options))//调用隐私方法验证参数,不合法则返回

returnthis;

functionfilter(options){//定义隐私函数,外界无法访问

//如果参数不存在,或者存在且为对象,则返回true,否则返回false

return!options||(options&&typeofoptions==="object")?true:false;

这样对于下面的非法参数设置,则会忽略该方法的调用,但是不会抛出异常。

$(function(){

$("p").color("#fff");

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

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

parent:function(){//扩展jQuery对象方法,获取所有匹配元素的父元素

vararr=[];

$.each(this,function(index,value){//遍历匹配的DOM元素

arr.push(value.parentNode);//把匹配元素的父元素推入临时数组

arr=$.unique(arr);//在临时数组中过滤重复的元素

returnthis.setArray(arr);//把变量arr打包为数组类型返回

在上面的 jQuery 对象方法中,通过遍历所有的匹配元素,获取每个 DOM 元素的父元素,并把这些父元素存储到一个临时数组中,通过过滤、打包再返回。

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

var$p=$("p");//获取所有p元素,并存储到变量$p中

$p.parent().css("border","solid1pxred");//调用parent()方法获取p元素的父元素,并设置它们的边框样式

<divstyle="width:400px;height:200px;">大盒子

<p>段落文本1</p>

<divstyle="width:200px;height:100px;">小盒子

<p>段落文本2</p>

</div>

如果在设置了父元素的边框后,我们希望把 jQuery 对象匹配的所有元素都隐藏起来,则可以添加下面的代码,则在浏览器中预览就会发现 div 元素也被隐藏起来了。

$p.hide();//隐藏所有p元素,即当前jQuery对象

也就是说,在上面代码中 $p 变量已经被修改,它不再指向当前 jQuery 对象,而是指向 jQuery 对象匹配元素的父元素,因此在为 $p 调用 hide() 方法时,就会隐藏 div 元素,而不是 p 元素。

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

对象方法进行优化,代码如下所示。

parent:function(options){//扩展jQuery对象方法,获取所有匹配元素的父元素

returnthis.pushStack(arr);//返回新创建的jQuery对象,而不是修改后的当前jQuery对象

这时,如果继续执行上面的演示实例操作,则可以看到 div 元素边框样式被定义为红色实现了,同时也隐藏了其包含的 p 元素。

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

$p.parent().css("border","solid1pxred").end().hide();

其中 end() 方法能够恢复被破坏的 jQuery 对象,也就是说 parent() 方法返回的是当前元素的父元素的集合,现在调用 end() 方法之后,又恢复到最初的当前元素集合,此时可以继续调用方法作用于原来的 jQuery 对象上了。

继续阅读