8.1.8 封装 jQuery 插件
上面几节就 jQuery 插件的创建方法进行了详细讲解,一般对外发布的自定义插件都应该进行封装,封装的插件还应该符合规范,只有这样所创建的插件才具有推广价值,并得到其他用户的喜爱。
封装 jQuery 插件的第一步是定义一个独立域,代码如下所示。
- <script type="text/javascript">
- (function($){
- // 自定义插件代码
- })(jQuery); // 封装插件
- </script>
确定创建插件类型,选择创建方式。例如,创建一个设置元素字体颜色的插件,则应该创建 jQuery 对象方法。考虑到 jQuery 提供了插件扩展方法 extend() ,调用该方法定义插件会更为规范。代码如下。
- (function($){
- $.extend($.fn, { // 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); // 封装插件
- <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>
- <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 = {
- // 扩展方法
- };
例如,继续以上一节的示例为基础,我们为其添加一个格式化的扩展功能,这样用户在设置颜色的同时,还可以根据需要适当进行格式化功能设计,如加粗、斜体、放大等功能操作。扩展的 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); // 封装插件
例如,下面的示例调用了 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>
下面我们就用这个新方法为所有 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>
- $(function(){
- var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中
- $p.parent().css("border", "solid 1px red"); // 调用 parent() 方法获取 p 元素的父元素,并设置它们的边框样式
- $p.hide(); // 隐藏所有 p 元素,即当前 jQuery 对象
- });
上面示例仅仅是破坏性操作的一种表现,如果要避免此类隐性修改 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>
针对上面的代码,我们就可以采用连续行为进行编写了,代码如下所示。
- $(function(){
- var $p = $("p"); // 获取所有p元素,并存储到变量 $p 中
- $p.parent().css("border", "solid 1px red").end().hide();
- });