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

8.1.8 封装 jQuery 插件

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

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

<script type="text/javascript">
(function($){
	// 自定义插件代码
})(jQuery); // 封装插件
</script>

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

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

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

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		color: function(options){	// 自定义插件名称
			var options = $.extend({ // 参数选项对象处理	
				bcolor: "white",	 // 背景色默认值
				fcolor: "black"		 // 前景色默认值
			}, options);
			return this.each(function(){ // 返回匹配的 jQuery 对象
				$(this).css("color", options.fcolor);		// 遍历设置每个 DOM 元素的字体颜色
				$(this).css("backgroundColor", options.bcolor);		// 遍历设置每个 DOM 元素的背景颜色
			});		
		}
	});
})(jQuery); // 封装插件

$(function(){	// 页面初始化
	$("h1").color({ // 设置标题的前景色和背景色
		bcolor: "#eea",
		fcolor: "red"
	});
});
</script>
</head>
<body>
<h1>标题文本</h1>

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

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

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

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		color: function(options){	
			var options = $.extend({}, $.fn.color.defaults, options);
			return this.each(function(){ // 返回匹配的 jQuery 对象
				$(this).css("color", options.fcolor);		// 遍历设置每个 DOM 元素的字体颜色
				$(this).css("backgroundColor", options.bcolor);		// 遍历设置每个 DOM 元素的背景颜色
			});		
		}
	});
	$.fn.color.defaults = {	// 独立设置 $.fn.color 对象的默认参数值
		bcolor: "white",
		fcolor: "black"
	};
})(jQuery); // 封装插件

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		color: function(options){	
			var options = $.extend({}, $.fn.color.defaults, options);
			return this.each(function(){ // 返回匹配的 jQuery 对象
				$(this).css("color", options.fcolor);		// 遍历设置每个 DOM 元素的字体颜色
				$(this).css("backgroundColor", options.bcolor);		// 遍历设置每个 DOM 元素的背景颜色
			});		
		}
	});
	$.fn.color.defaults = {	// 独立设置 $.fn.color 对象的默认参数值
		bcolor: "white",
		fcolor: "black"
	};
})(jQuery); // 封装插件

$(function(){	// 页面初始化
	$.fn.color.defaults = { // 预设默认的前景色和背景色
		bcolor: "#eea",
		fcolor: "red"
	};
	$("h1").color();
	$("p").color({bcolor: "#fff"}); // 为段落文本设置默认色,同时覆盖背景色为白色
	$("div").color(); // 为盒子设置默认色
});
</script>
</head>
<body>
<h1>标题文本</h1>
<p>段落文本</p>
<div>盒子</div>
</body>

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

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

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

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

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

(function($){
	$.extend($.fn, {
		color: function(options){	
			var options = $.extend({}, $.fn.color.defaults, options);  // 覆盖原来参数
			return this.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.defaults = {	// 独立设置 $.fn.color 对象的默认参数值
		bcolor: "white",
		fcolor: "black"
	};
	$.fn.color.format = function(str){	// 开放的功能函数
		return str;
	}
})(jQuery); // 封装插件
在上面的示例中,通过开发的方式定义了一个 format() 功能函数,在这个功能函数中默认没有进行格式化设置,然后在 color() 函数体内利用这个开放性功能函数格式化当前元素内的 HTML 字符串。

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		color: function(options){	
			var options = $.extend({}, $.fn.color.defaults, options);  // 覆盖原来参数
			return this.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.defaults = {	// 独立设置 $.fn.color 对象的默认参数值
		bcolor: "white",
		fcolor: "black"
	};
	$.fn.color.format = function(str){	// 开放的功能函数
		return str;
	}
})(jQuery); // 封装插件

$(function(){	// 页面初始化
	$.fn.color.defaults = { // 预设默认的前景色和背景色
		bcolor: "#eea",
		fcolor: "red"
	};
	$.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本加粗显示
		return "<strong>" + str + "</strong>";
	};
	$("h1").color();
	$("p").color({bcolor: "#fff"}); // 为段落文本设置默认色,同时覆盖背景色为白色
	$.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本放大显示
		return "<span style='font-size: 30px;'>" + str + "</span>";
	};
	$("div").color(); // 为盒子设置默认色
});
</script>
</head>
<body>
<h1>标题文本</h1>
<p>段落文本</p>
<div>盒子</div>
</body>
上述技巧让用户能够传递自己的功能设置,以覆盖插件默认的功能,从而方便了其他用户以当前插件为基础进一步去扩写插件。

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

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

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

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		color: function(options){	
			if(!filter(options))	// 调用隐私方法验证参数,不合法则返回
				return this;
			var options = $.extend({}, $.fn.color.defaults, options);  // 覆盖原来参数
			return this.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.defaults = {	// 独立设置 $.fn.color 对象的默认参数值
		bcolor: "white",
		fcolor: "black"
	};
	$.fn.color.format = function(str){	// 开放的功能函数
		return str;
	}
	function filter(options){ // 定义隐私函数,外界无法访问
		// 如果参数不存在,或者存在且为对象,则返回 true ,否则返回 false
		return !options || (options && typeof options === "object")?true: false;
	}
})(jQuery); // 封装插件

$(function(){	// 页面初始化
	$.fn.color.defaults = { // 预设默认的前景色和背景色
		bcolor: "#eea",
		fcolor: "red"
	};
	$.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本加粗显示
		return "<strong>" + str + "</strong>";
	};
	$("h1").color();
	$("p").color({bcolor: "#fff"}); // 为段落文本设置默认色,同时覆盖背景色为白色
	$.fn.color.format = function(str){ // 扩展 color() 插件的功能,使内部文本放大显示
		return "<span style='font-size: 30px;'>" + str + "</span>";
	};
	$("div").color(); // 为盒子设置默认色
});
</script>
</head>
<body>
<h1>标题文本</h1>
<p>段落文本</p>
<div>盒子</div>
</body>
这样对于下面的非法参数设置,则会忽略该方法的调用,但是不会抛出异常。

$(function(){
	$("p").color("#fff");
});

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

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

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

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

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		parent: function(){ // 扩展 jQuery 对象方法,获取所有匹配元素的父元素
			var arr = [];
			$.each(this, function(index, value){ // 遍历匹配的 DOM 元素
				arr.push(value.parentNode); // 把匹配元素的父元素推入临时数组
			});
			arr = $.unique(arr);    // 在临时数组中过滤重复的元素
			return this.setArray(arr); // 把变量 arr 打包为数组类型返回
		}
	});
})(jQuery); // 封装插件

$(function(){
	var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中
	$p.parent().css("border", "solid 1px red");  // 调用 parent() 方法获取 p 元素的父元素,并设置它们的边框样式
});
</script>
</head>
<body>
<div style="width: 400px; height: 200px;">大盒子
	<p>段落文本1</p>
	<div style="width: 200px; height: 100px;">小盒子
		<p>段落文本2</p>
	</div>
</div>
</body>
如果在设置了父元素的边框后,我们希望把 jQuery 对象匹配的所有元素都隐藏起来,则可以添加下面的代码,则在浏览器中预览就会发现 div 元素也被隐藏起来了。

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

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

<script type="text/javascript">
(function($){
	$.extend($.fn, {
		parent: function(options){ // 扩展 jQuery 对象方法,获取所有匹配元素的父元素
			var arr = [];
			$.each(this, function(index, value){ // 遍历匹配的 DOM 元素
				arr.push(value.parentNode); // 把匹配元素的父元素推入临时数组
			});
			arr = $.unique(arr);    // 在临时数组中过滤重复的元素
			return this.pushStack(arr); // 返回新创建的 jQuery 对象,而不是修改后的当前 jQuery 对象
		}
	});
})(jQuery); // 封装插件

$(function(){
	var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中
	$p.parent().css("border", "solid 1px red");  // 调用 parent() 方法获取 p 元素的父元素,并设置它们的边框样式
	$p.hide();   // 隐藏所有 p 元素,即当前 jQuery 对象
});
</script>
</head>
<body>
<div style="width: 400px; height: 200px;">大盒子
	<p>段落文本1</p>
	<div style="width: 200px; height: 100px;">小盒子
		<p>段落文本2</p>
	</div>
</div>
</body>
这时,如果继续执行上面的演示实例操作,则可以看到 div 元素边框样式被定义为红色实现了,同时也隐藏了其包含的 p 元素。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值