「jQuery插件开发日记」如何建立一个基础的插件 - [翻译]
How to Create a Basic Plugin, 翻译自 jQuery 官方网站。
如何建立一个基础的插件
有时,你想要在你的代码里面实现一个可复用的功能。
举个例子,如果你想要在一个 jQuery 对象上调用一个简单的方法,来对这个对象进行一系列的操作,那么你应该写一个 jQuery 插件。
jQuery是如何工作的
在开始写插件之前,我们必须对 jQuery 的工作方式有些许的认识。来看下面的代码:
$( "a" ).css( "color", "red" );这是一句非常基础的 jQuery 代码,但是你知道它背后发生了什么吗?当你使用 $ 去选择元素的时候,它就会返回一个 jQuery 对象。这个对象包含可用的所有方法(.css(), .click()等)和所有符合你传入选择器的元素。
这个对象从 $.fn 对象继承所有的方法。 $.fn 对象包括所有 jQuery 对象的方法,也就是说,如果你想添加自己的方法的话,你需要将方法添加到 $.fn 对象上。
※ 译者注 ※
$.fn == $.prototype // true
建立一个基础的插件
假设我们现在要建立一个让元素里的文字变绿的插件。
我们需要做的只是在 $.fn 里添加一个方法 greenify。
$.fn.greenify = function() { this.css( "color", "green" );}; $( "a" ).greenify(); // Makes all the links green.注意到我们在这段代码里面用的是 this 而不是 $(this)。这是因为在 greenify 函数中,this 指代的是 jQuery 对象,所以可以使用 jQuery 的所有方法。
链式操作
jQuery 最大的特点之一就是支持链式操作。链式操作的实现是靠 jQuery 所有的方法返回 jQuery 对象来实现的(也有一些例外,比如无参数的 .width() 返回的就是宽度)。
只需要增加一行代码,我们就可以让我们的插件变得可以链式操作:
$.fn.greenify = function() { this.css( "color", "green" ); return this;}; $( "a" ).greenify(); // Makes all the links green.命名空间
$ 这个变量在很多 JavaScript 库里都很受欢迎。如果你一起使用 jQuery 和其他的库,你可能就得用 jQuery.noConflict() 来让 jQuery 返还对 $ 变量的控制权了。
然而由于 $ 变量已经不是 jQuery 了,我们的插件就没办法工作了。要让我们的插件继续工作,并且也可以和其他的库一起使用,我们需要将我们的代码放入一个即时执行函数表达式中,然后将jQuery当做参数传入,命名为 $:
(function ( $ ) { $.fn.greenify = function() { this.css( "color", "green" ); return this; }; }( jQuery ));除了上面提到的两个功能,即时执行函数增加了一层作用域,让我们可以定义自己的私有变量。例如:
(function ( $ ) { var shade = "#556b2f"; $.fn.greenify = function() { this.css( "color", shade ); return this; }; }( jQuery ));※ 译者注 ※
jQuery.noConflict()的作用是返还$的控制权。为什么说是返还呢?因为 jQuery 在初始化的时候,会记录
$这个变量,然后再将$绑定为自身。
所以调用jQuery.noConflict(),实际上是将$赋值为之前记录的旧值。
减少绑定方法的数量
当编写插件时,我们最好只在 $.fn 上绑定一个方法。这不仅减少了其他的插件覆盖你的插件的几率,也减少了你的插件覆盖其他插件的几率。
使用 each() 方法
一般来说,使用选择器选择的 jQuery 对象都是一个集合。如果你想对这里的某些元素进行操作(比如获取属性,计算位置等),你就需要用到 .each() 方法来进行遍历。
$.fn.myNewPlugin = function() { return this.each(function() { // Do something to each element here. }); };※ 译者注 ※
有读者可能会问,在之前的代码中.css()并没有进行遍历,如何使所有元素里的问题都变成绿色?
这是因为.css()方法里已经做了这个遍历的工作了。
注意到我们返回的是 .each() 的结果,而不是 this。这是因为 .each() 也是可链式操作的, 它返回的也是 this。不过这样写更加契合链式操作的思想。
接受用户配置
当你的插件变得越来越复杂的时候,你最好通过接受用户的配置来让用户定制某些功能。
最简单的方法就是使用一个对象来储存配置。
(function ( $ ) { $.fn.greenify = function( options ) { // This is the easiest way to have default options. var settings = $.extend({// These are the defaults.color: "#556b2f",backgroundColor: "white" }, options ); // Greenify the collection based on the settings variable. return this.css({color: settings.color,backgroundColor: settings.backgroundColor }); }; }( jQuery ));/* Example usage */$( "div" ).greenify({ color: "orange"});默认的颜色值 #556b2f 被用户定义的值 orange 覆盖。
※ 译者注 ※
x.extend([deep], target [,obj1][,objN])
.extend()方法主要是将所有obj的属性和方法全部添加到target上。同名的会覆盖,而null和undefined会被忽略。
当只有一个参数的时候,x就变成了target,也就是将这个唯一的参数的所有方法和属性全部添加到x中。
x可以是:
$
$.fn
$.extend()和$.fn.extend()的不同也就非常明显了。
完整的例子
(function( $ ) { $.fn.showLinkLocation = function() { this.filter( "a" ).append(function() {return " (" + this.href + ")"; }); return this; }; }( jQuery ));HTML 结构:
<!-- Before plugin is called: --><a href="page.html">Foo</a> <!-- After plugin is called: --><a href="page.html">Foo (page.html)</a>※ 译者注 ※
上面的例子里,this.filter()和this.href的this是不同的。前者是 jQuery 对象,后者是 HTMLElement 对象。var $links = $('a');$links[0] instanceof HTMLElement; // true$links.slice(0, 1) instanceof jQuery; // true$links.each(function(){ this instanceof HTMLElement; // true});

