jquery很棒,它跨浏览器,简单易学,可以轻而易举的给网站增加交互性,而且有非常多的插件来帮助你完成你想做的事情.
但是,如果你找不到刚好适合你站点的插件怎么办?也许你只是通过合并一些常用的函数到一个干净整洁的package里面?解决方案也许是用你自己的插件来完全的满足需求.
首先,编写自己的jquery plugin不想它看起来那么难,下面这个例子将会贯穿编写一个简单的plugin的整个过程--增加一些可选项options,甚至有回调函数callback.
准备架构(settting up)
我们将用一个老掉牙的编程例子--hello world作为开始.在开始实现之前,我们需要建立文件并链接到html文档当中.首先,我们需要创建我们的plugin文件并放在站点的js文件夹下.用"jquery"做文件名开头,后面跟着plugin名称是一个预定俗称的东西,所以我们叫它:"jquery.hello-world.js":
接下来,我们需要确认plugin文件,当然还有jquery核心文件,都被连接到html文件中,所以把下面的两行代码放到html文档的</body>标签前面的底部.
<script src="js/jquery-1.9.1.min.js"></script>
<script src="js/jquery.hello-world.js"></script>
jquery插件结构(jquery plugin structure)
jquery自带了非常简单的用来自定义plugin所必须的所有钩子(hooks).但是为了保持一个良好的javascript代码习惯,我们仍要警惕,要确保所有的代码都本地域中(local scope),我们将用一个经典的jquery插件结构作为开始:
(function($) {
$.fn.helloWorld = function() {
// Future home of "Hello, World!"
}
}(jQuery));
让我们花一点点时间来了解一下发生了什么,通过(function(){})把所有代码都包裹了起来的js闭包模式,使我们为了确保插件中的所有变量都不会污染全局命名空间.我们不想和之后页面中引用的其他js代码发生冲突.另一件你可能已经注意到的事情是我们在定义插件的时候jquery好像处于"non-conflic"模式.再一次的,我们是在尽量避免和页面中的其他js发证碰撞(colliding).因此我们想要确保,我们的插件不依赖jquery默认的$,$可能也被其他框架使用了.
最后,$.fn是jquery允许自定义插件的一种方式,我们已经命名为helloworld.让我们用上面的代码片段实现一些真正的功能吧!
让我们的插件开始干活(making our plugin do something)
我们要让插件干一件很蠢的事情,但也同时足够简单的说明我们的目的,就是把所有插件作用上的元素的文本都改成"Hello,World".
(function($) {
$.fn.helloWorld = function() {
this.each( function() {
$(this).text("Hello, World!");
});
}
}(jQuery));
当我们传一个jquery选择器来调用插件的时候,插件作用的对象已经是一个jquery对象了,所以我们不需要用$(this)的结构来转换它.然而,一旦我们开始循环选择器匹配的每个实例,在使用$.each()的任何一次循环中都要用$(this)结构转换.
假设我们想转换下面页面中<h2>的文本:
调用插件的方式你已经很熟悉了,就象这样:
<script>
$(document).ready( function() {
$('h2').helloWorld();
});
</script>
得到的结果是:
我们距离完成还尚早.当我们的插件从技术的角度上工作了之后,它只是生活在自己孤立的小世界中.如果你试着用链式编程在后面在增加一个jquery动作时,你会发现什么都没有发生,因为我们的插件有一个dead end(没有return).为了修复它,一定要确保把插件循环dom元素的结果return:
(function($) {
$.fn.helloWorld = function() {
return this.each( function() {
$(this).text("Hello, World!");
});
}
}(jQuery));
恭喜你!你已经完成了你自己的第一个jquery插件!
等一下,还没完那!(but wait,there's more)
你现在很高兴完成了自己的jquery插件.可是你的boss回来说,他们需要把站点翻译成西班牙语(spanish),我们现在该怎么办?
好吧,我们可以从增加一个参数(argument)开门,把上面的代码拿下来,我们可以用一个变量来代替文本并在调用插件的时候传进来来替代把文本硬编码(hard-coding)到插件中:
(function($) {
$.fn.helloWorld = function( customText ) {
return this.each( function() {
$(this).text( customText );
});
}
}(jQuery));
好了,现在我们可以传递喜欢的任意文本到插件中,我们在马德里的同事已经把我们需要的翻译好的西班牙语文本给我们了,现在我们把它当作参数传给插件:
<script>
$(document).ready( function() {
$('h2').helloWorld('¡Hola, mundo!');
});
</script>
我们的页面现在看起来像这样:
完成插件自定义,胜利在望!(complete customzination FTW)
但是我感觉到有点不安,如果调用插件时没有传递文本,会怎么样?就向代码写的那样,插件只会把所匹配的元素的文本置空.但是也许我们不想这样.并且,我们boss已经回来过一次让我们增加了一个变量了,如果boss想要更多可定制选项怎么办?从长远来看,无休止的增加变量是行不通的,一个一劳永逸的方法是options对象.
我们已经知道指定的文本是需要可定制化的,现在你的boss又要求文本的颜色和字体样式也可定制.让我们给插件增加一个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));
现在我们有一个setting对象,当调用插件时没有任何参数,setting对象就会使用我们建立好的默认值文本.setting对象还有另外两个属性,color和fontstyle,默认值是null.这个插件里我们不需要在展示的文本中建立任何的颜色和fontstyle 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 );
}
});
自从这个插件的目的是替换文本以来,无论提供的文本是什么,都会替换,这也是为什么我们提供了一个默认值以便在没有提供任何options时可以正常显示.但是color和fontstyle是测试用的,只有提供值了才会生效.
我们的boss回来又说,他们需要把站点翻译成法语,并且希望文本是蓝色斜体字.幸运的是,我们的插件现在能满足所有需求了:
$('h2').helloWorld({
text : 'Salut, le monde!',
color : '#005dff',
fontStyle : 'italic'
});
我们现在又一个包含若干参数对象的插件,并且有在将来轻松增加更多参数不影响之前插件使用者的良好能力.可维护性万岁!
我们的boss又带着另外一个需求回来了:一旦插件完成了我们交给他的任何任务,插件应该像一个alert通知我们这件事(完成事件).面对这个需求,我们有两个选择:要么罢工不干--因为boss很明显从来没听过一个叫需求文档的东西(意思是:boss的需求经常变,不固定)要么不走罢工那么极端的路线,让我们的插件有能力处理callback回调函数.
回调函数对js函数来说是可选的,回调函数也是js函数.同样对jquery来说不是唯一的.再一次,回调函数也许看起来像一件很复杂的事情,但是实际上简单的很.
我们需要做的仅仅是在options对象中新建立一个变量:
// Establish our default settings
var settings = $.extend({
text : 'Hello, World!',
color : null,
fontStyle : null,
complete : null
}, options);
现在我们有一个complete变量用来展示一个动作当插件完成它的动作的时候,为了条用它,我们要确保我们传递给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!' ) }
});
作为一个结果,我们将会看见回调函数已经被插件执行了:
结束语(Conclusion)
当你知道要在js里面重复的做许多同样的事的时候,自定义jquery插件是一个不错的选择.通过编写自己的插件,即保持了js代码的整洁,也保持了全局命名空间的整洁干净.
如果你想用例子的代码继续"耍耍",你可以从我的my GitHub上得到.
翻译自:http://blog.teamtreehouse.com/writing-your-own-jquery-plugins