(翻译)编写属于你的jQuery插件

Writing Your Own jQuery Plugins

原文地址:http://blog.teamtreehouse.com/writing-your-own-jquery-plugins

jQuery是一个优秀的javascript库。它不但拥有良好的跨浏览器兼容性,容易学习掌握,还可以轻而易举的为你的网站添加有趣的交互。同时,大量的jQuery插件能够让你随心所欲的完成你想要完成的工作。

然而在平常的工作中,并不是总能够找到可以完全满足需求的插件,或者你想要将一些常用的功能封装以便保证项目的可重用性。在这些情形下,编写自己的jQuery插件是一个很好的满足需求的手段。

其实,编写jQuery插件并没有想像中难。这篇文章,我们将通过编写一个简单的插件,为其添加一些参数和事件回调,来向你完整的展示整个流程。

准备工作

像大多数编程指南的开篇一样,我们也以一个“Hello World”插件开始吧。在开始之前,先创建必要的文件并在HTML文档中引用它们。首先,在站点的js目录下新建插件文件jquery.hello-world.js。通常,jQuery插件的命名以“jquery.”开始。

(翻译)编写属于你的jQuery插件

接下来,需要在HTML文件中链接插件文件及其依赖的jquery核心库。我们将以下两行放置在文档的底部,也即</body>标签之前。

<script src="js/jquery-1.9.1.min.js"></script>
<script src="js/jquery.hello-world.js"></script>

jQuery插件的结构

jQuery本身已经为插件开发提供了足够的支持。但我们依然需要遵循JavaScript开发的最佳实践,将所有的代码都囊括在一个本地作用域中。下面是一个常规的jQuery插件基础结构:

(function($) {
$.fn.helloWorld = function() { // Future home of "Hello, World!" }
}(jQuery));

让我们花一点时间来理解一下上述代码的含义。通过将所有代码包含在 (function() {}) 自执行代码块中,能够保证插件内部的所有变量不会污染到全局变量。毕竟,我们肯定不愿意看到编写的插件和页面中的其它JavaScript代码发生冲突。

可能你已经注意到,我们定义插件的方式,就好像jQuery也位于其“无冲突”模式。再一次强调,我们需要避免插件与页面中其它JavaScript发生冲突,这也包括使用到的'$'符号。该符号很有可能被其它的JavaScript库使用。

最后,$.fn这种写法是jQuery定义插件的方式。这里将其命名为helloWorld并将所有代码放置在该函数中。下面,让我们做一些真正有意义的工作。

插件代码编写

为了使插件的代码尽可能的简单,同时又能达到演示的目的,我们将要完成的功能是改变关联的元素的文本内容为“hello world”。这已经近乎于傻瓜式的操作了。

(function($) {

    $.fn.helloWorld = function() {

        this.each( function() {
$(this).text("Hello, World!");
}); } }(jQuery));

当在jQuery选择器上调用上述插件时,关联的元素已经是一个jQuery对象,因此不再需要再用"$(this)"结构进行包装。然而,如果需要在一个循环中使用每一个匹配的元素,比如$.each(),则一般会使用$(this)对循环的元素进行包装。

假如我们想要改变下面页面中所有的<h2>标题的文本:

(翻译)编写属于你的jQuery插件

调用的方式你已经很熟悉了,就像这样:

<script>
$(document).ready( function() {
$('h2').helloWorld();
});
</script>

结果为:

(翻译)编写属于你的jQuery插件

我们能做的远远不止如此。当上面的插件调用时,实际上完全被隔离在了它本身的作用域。也就是说,调用的作用链在插件内部被终止了。如果你试着以jQuery的链式写法调用其它操作时,是不会生效的。为了修复这个问题,需要确保返回每个DOM元素循环后的结果:

(function($) {

    $.fn.helloWorld = function() {

        return this.each( function() {
$(this).text("Hello, World!");
}); } }(jQuery));

恭喜你,已经写出了你人生中的第一个jQuery插件!

稍安勿躁,好戏在后头!

现在你可能正沉浸在编写出自己的jQuery插件的喜悦中,这时候你的老大跑过来说,想要把现在的网站翻译成西班牙语。老天爷,现在该咋整?

其实,可以简单的添加一个参数完成上述需求。重新审视上述插件的代码,我们可以替换其内部的硬编码文本内容为一个变量,在调用的时候传入该变量的值。

(function($) {

    $.fn.helloWorld = function( customText ) {

        return this.each( function() {
$(this).text( customText );
}); } }(jQuery));

如你所见,在调用的时候可以传入任何想要传入的文本值。办公室的马德里先生已经将我们需要的东西翻译成了西班牙语,现在可以使用参数的方式调用该插件:

<script>
$(document).ready( function() {
$('h2').helloWorld('¡Hola, mundo!');
});
</script>

页面呈现如下:

(翻译)编写属于你的jQuery插件

完全*的控制

可能你已经感觉到一点担忧,如果在调用插件的时候没有传入文本怎么办?显而易见,页面中匹配的元素的文本会被插件修改为空值,这可能不是我们预期的结果。另外,我们的老大已经跑过来要求添加一个变量,如果他再跑过来要求添加更多的可定制化的选项怎么办?不断的添加参数毕竟不是长远之计,一劳永逸的解决方法是通过options对象。

我们已经知道具体的文本内容需要由外部传入,现在,老大又要求文本颜色和字体大小同样在调用时指定(好吧,我知道颜色和字体大小都可以通过jQuery内置的函数进行控制,这里仅仅是一个演示)。让我们为插件添加一个options参数对象,同时通过$.extend方法与默认的参数进行合并:

(function($) {

    $.fn.helloWorld = function( options ) {

        // Establish our default settings
var settings = $.extend({
text : 'Hello, World!',
color : null,
fontStyle : null
}, options); return this.each( function() {
// We'll get back to this in a moment
}); } }(jQuery));

现在我们有了可供外部设置的选项。如果调用的时候缺少了待替换的文本,将会使用指定的默认值。另外,在配置参数中添加了"color"和"fontStyle"两个属性,默认值均为"null"。如果在CSS文件中已经定义了颜色值和字体大小,在插件调用时不需要再重复指定。当然,二者均可以在插件内部重写。代码如下:

return this.each( function() {
$(this).text( settings.text ); if ( settings.color ) {
$(this).css( 'color', settings.color );
} if ( settings.fontStyle ) {
$(this).css( 'font-style', settings.fontStyle );
}
});

因为该插件的目的是替换文本。如果提供的文本值为空则没有任何意义,这也是在插件内部为其指定默认值的原因,当参数缺失时显示缺省值总比空白要好得多。另一方面,颜色值和字体风格则只需要提供一个可供重写的入口。

这时候老大跑过来说想要将站点翻译为法语,另外还希望将文本颜色改为蓝色,字体调整为italic。幸运的是,我们的插件处理这些需求毫无压力:

$('h2').helloWorld({
text : 'Salut, le monde!',
color : '#005dff',
fontStyle : 'italic'
});

(翻译)编写属于你的jQuery插件

你看,我们的插件已经拥有了一些由外部控制的参数。将来可以在不影响现有调用的基础上,非常容易的添加或去掉某个参数。这些都益于良好的可扩展性。

但我们的老大带着另外一个需求再一次跑了过来:一旦插件按其预定的方式调用完毕,应该弹出一个对话框通知用户这个消息。为了达到这个目的,有两个选择:要么辞职,因为这个家伙明显无视最初的需求文档。要么不走这么极端的路线,为我们的插件添加处理回调函数的能力。

回调函数是JavaScript函数的参数,同时它本身也是一个JavaScript函数。其并不是jQuery独有的特性。初看上去回调函数是一个复杂的概念,但实际它的使用非常简单。

我们需要做的仅仅是在配置对象中附加一个额外的参数:

// Establish our default settings
var settings = $.extend({
text : 'Hello, World!',
color : null,
fontStyle : null,
complete : null
}, options);

现在,当插件调用完毕时,就该附加的“complete”参数生效了。为了调用该参数,首先需要确保其是一个真正的函数。幸运的是,jQuery已经内置了$.isFunction函数帮助我们完成这个检测:

return this.each( function() {
// Our plugin so far if ( $.isFunction( settings.complete ) ) {
settings.complete.call( this );
}
});

页面中调用如下:

$('h2').helloWorld({
text : 'Salut, le monde!',
color : '#005dff',
fontStyle : 'italic',
complete : function() { alert( 'Done!' ) }
});

最终的效果,插件执行完毕后会触发回调函数:

(翻译)编写属于你的jQuery插件

结语

当你一遍又一遍的写着大量重复的JavaScript代码,自定义jQuery插件是一种很好摆脱这种泥沼的方式。通过自定义插件,你在保持自己代码DRY(dont repeat yourself)的同时,也确保了全局名称空间的独立和纯度。

如果你想调试上述示例代码,可以在我的 GitHub 获取。

上一篇:jQuery用面向对象的思想来编写验证表单的插件


下一篇:Linux 用户与用户组