有时候,你希望有一块功能在整个代码当中都可以使用。例如,你可能想要有一个单一的方法可以在jQuery选择器上进行调用,用于处理该选择器上的一系列操作。又或许你编写了一个十分有用的工具函数,并希望能够简单的迁移到其它的项目当中。在这种情况下,你也许想要编写一个插件。
jQuery工作原理101:jQuery对象方法和工具方法
在我们编写插件前,首先需要对jQuery的工作原理有一些了解。看一下这段代码:
1 $( "a" ).css( "color", "red" );
这是一段相当基础的jQuery代码,但你知道在幕后都发生了什么吗?每当你使用那个 $ 方法选择一个元素时,它返回一个jQuery对象。该对象包含了所有你已经用过的方法(.css(), .click(), etc.)以及所有与选择器相符的元素。jQuery对象从 $.fn 对象获得这些方法。该对象包含了所有jQuery对象的方法,并且如果我们想要编写自己的方法,它也会同样包含这些方法。
此外,jQuery工具方法 $.trim() 被用来将用户输入内容中的任意前置或后置空白字符移除。工具方法是指那些直接驻留在 $ 方法自身的方法。当你所扩展的jQuery API不需要对你所取回的DOM元素进行操作时,你或许就会想要编写一个工具方法插件。
基本插件编写
比如说我们想要创建一个插件,用于将一组取回元素中的文本变成绿色。我们所需要做的就是添加一个名为 greenify 的方法到 $.fn 当中,之后你将可以像使用其他jQuery对象方法那样使用它。
1 $.fn.greenify = function() { 2 this.css( "color", "green" ); 3 }; 4 5 $( "a" ).greenify(); // Makes all the links green.
要注意的是,在使用另一个方法 .css() 时,我们使用了 this 而不是 $(this) 。这是因为我们的 greenify 方法和 .css() 同属于一个对象。
连缀
插件已经可以使用了,但是想要让我们的插件真正用于实战,还有几件事情需要我们来完成。连缀是jQuery的特性之一,它使你可以在一个选择器上连接五个或是更多的操作。这些都是通过将所有的jQuery对象方法再次返回到原始的jQuery对象来实现的(但也有一些例外:调用不带参数的 .width() 方法将返回被选中元素的宽度,并且它是无法进行连缀的)。下面使用一行代码使我们的插件方法可以进行连缀:
1 $.fn.greenify = function() { 2 this.css( "color", "green" ); 3 return this; 4 } 5 6 $( "a" ).greenify().addClass( "greenified" );
注意,连缀的概念并不适用于jQuery工具方法,例如:$.trim()。
保护 $Alias 并添加作用域
在JavaScript类库当中, $ 变量十分的流行,如果你在使用jQuery的同时又使用其它的类库,你或许将不得不使用 jQuery.noConflict() 方法来使jQuery不再使用 $ 变量。然而,这将会打乱我们的插件,因为它是在假定 $ 是jQuery的别名的情况下编写的。为了可以兼容其它的插件,并且仍旧使用jQuery的 $ 别名,我们需要将所有的代码放置在立即调用的函数表达式(Immediately Invoked Function Expression)当中,然后传递jQuery函数,并将 $ 作为参数:
1 (function ( $ ) { 2 3 $.fn.greenify = function() { 4 this.css( "color", "green" ); 5 return this; 6 }; 7 8 $.ltrim = function( str ) { 9 return str.replace( /^\s+/, "" ); 10 }; 11 12 $.rtrim = function( str ) { 13 return str.replace( /\s+$/, "" ); 14 }; 15 16 }( jQuery ));
此外,立即调用的函数表达式的主要作用是允许我们拥有自己的私有变量。假设我们需要不同的绿色,并且想要把它存储到一个变量当中。
1 (function ( $ ) { 2 3 var shade = "#556b2f"; 4 5 $.fn.greenify = function() { 6 this.css( "color", shade ); 7 return this; 8 }; 9 10 }( jQuery ));
最小化插件足迹
当编写插件时,最佳的实践是只占用一个 $.fn 扩展槽。这会降低插件被覆盖的几率,同时也会降低覆盖其它插件的几率。换句话说,下面的做法是不恰当的:
1 (function( $ ) { 2 3 $.fn.openPopup = function() { 4 // Open popup code. 5 }; 6 7 $.fn.closePopup = function() { 8 // Close popup code. 9 }; 10 11 }( jQuery ));
更为合理的做法应当是只使用一个扩展槽,使用参数来控制扩展槽进行什么操作。
1 (function( $ ) { 2 3 $.fn.popup = function( action ) { 4 5 if ( action === "open") { 6 // Open popup code. 7 } 8 9 if ( action === "close" ) { 10 // Close popup code. 11 } 12 13 }; 14 15 }( jQuery ));
使用 each() 方法
标准的jQuery对象将包含对任意数量DOM元素的引用,这就是为什么jQuery对象通常被当成集合。如果你想对具体的元素集进行任意操作(例如,得到一个data属性,计算具体的属性等)此时你就需要使用 .each() 方法来遍历元素集。
1 $.fn.myNewPlugin = function() { 2 3 return this.each(function() { 4 // Do something to each element here. 5 }); 6 7 };
需要注意的是,我们返回 .each() 方法的结果而非返回 this 。因为 .each() 方法是可连缀的,它将返回 this ,和我们直接返回 this 是一样的。这是目前为止我们进行的保持连缀的最佳做法。
接受选项
当插件变得越来越复杂时,让插件可以通过选项进行自定义是一个不错的想法。尤其是当选项数量较多时,最简单的方式是使用对象字面量(object literal)。让我们对插件进行一些修改,使其能够接受一些选项。
1 (function ( $ ) { 2 3 $.fn.greenify = function( options ) { 4 5 // This is the easiest way to have default options. 6 var settings = $.extend({ 7 // These are the defaults. 8 color: "#556b2f", 9 backgroundColor: "white" 10 }, options ); 11 12 // Greenify the collection based on the settings variable. 13 return this.css({ 14 color: settings.color, 15 backgroundColor: settings.backgroundColor 16 }); 17 18 }; 19 20 }( jQuery ));
使用范例:
1 $( "div" ).greenify({ 2 color: "orange" 3 });
color 的默认值 #556b2f 通过 $.extend() 方法被覆盖成了橙色。
完整实现
下面是一个小插件的范例,它使用了我们之前所讨论的一些技术:
1 (function( $ ) { 2 3 $.fn.showLinkLocation = function() { 4 5 return this.filter( "a" ).each(function() { 6 $( this ).append( " (" + $( this ).attr( "href" ) + ")" ); 7 }); 8 9 }; 10 11 }( jQuery )); 12 13 // Usage example: 14 $( "a" ).showLinkLocation();
这个便利的插件将遍历集合中的所有锚点,然后将 href 属性附加到括号当中。
1 <!-- Before plugin is called: --> 2 <a href="page.html">Foo</a> 3 4 <!-- After plugin is called: --> 5 <a href="page.html">Foo (page.html)</a>
该插件还可以进行如下的优化:
1 (function( $ ) { 2 3 $.fn.showLinkLocation = function() { 4 5 return this.filter( "a" ).append(function() { 6 return " (" + this.href + ")"; 7 }); 8 9 }; 10 11 }( jQuery ));
我们将使用 .append() 方法的特性来接受一个回调函数,并且该回调函数的返回值取决于集合中的每个元素各自所附加的内容。要注意的是,与此同时同时我们并没有使用 .attr() 方法来取得 href 属性,因为原生的DOM API让我们可以轻松的访问已合理命名的 href 属性。