您的位置:首页 > Web前端 > JQuery

jQuery插件开发

2015-10-28 17:18 513 查看

jQuery插件的种类

封装对象方法

将对象方法封装起来,对通过选择器获取的jQuery对象添加方法,是最常见的一种插件。此类插件可以发挥出jQuery选择器的强大优势,有相当一部分的jQuery的方法,都是在jQuery脚本库内部通过这种形式“插”在内核上的,例如parent()方法,appendTo()方法等。

封装全局函数

可以将独立的函数加到jQuery命名空间下(jQuery全局函数就是属于jQuery命名空间的函数),即给jQuery类本身添加方法,可以理解为添加静态方法。如常用的jQuery.ajax()方法、去首尾空格的jQuery.trim()方法,都是jQuery内部作为全局函数的插件附加到内核上去的。

选择器插件

虽然jQuery的选择器十分强大,但在少数情况下,还是会需要用到选择器插件来扩充一些自己喜欢的选择器。

jQuery插件的机制

jQuery插件的机制很简单,就是利用jQuery提供的
jQuery.fn.extend(Object)
jQuery.extend(Object)
方法,扩展jQuery的功能,Object对象的“名/值对”分别代表“函数或方法名/函数主体”。

1.
jQuery.fn.extend(Object)
,多用于扩展上面提到的3种类型中的第一种,即封装对象方法。

首先我们来看fn 是什么东西呢。查看jQuery代码,就不难发现。

jQuery.fn = jQuery.prototype = {
   init: function( selector, context ) {.....};
};


原来
jQuery.fn = jQuery.prototype
,也就是jQuery对象的原型。那
jQuery.fn.extend()
方法就是扩展jQuery对象的原型方法。扩展原型上的方法,就相当于为对象添加”成员方法“,类的”成员方法“要类的对象才能调用,所以使用
jQuery.fn.extend(object)
扩展的方法, jQuery类的实例可以使用这个“成员函数”。


2. jQuery.extend(Object)用于扩展jQuery类本身,也就是在jQuery类/命名空间上增加新函数,或者叫静态方法,例如jQuery内置的 ajax方法都是用jQuery.ajax()这样调用的,有点像 “类名.方法名” 静态方法的直接调用方式。用于扩展后两种插件。

重载版本:
jQuery.extend([deep], target, object1, [objectN])


用一个或多个其他对象来扩展一个对象,返回被扩展的对象。

如果不指定target,则给jQuery命名空间本身进行扩展。这有助于插件作者为jQuery增加新方法。

如果第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象。否则的话,副本会与原对象共享结构。

未定义的属性将不会被复制,然而从对象的原型继承的属性将会被复制。

参数:

- deep: 可选。如果设为true,则递归合并。

- target: 待修改对象。

- object1: 待合并到第一个对象的对象。

- objectN: 可选。待合并到第一个对象的对象。

示例1:

//合并 settings 和 options,修改并返回 settings。
var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
jQuery.extend(settings, options);

//结果:
settings == { validate: true, limit: 5, name: "bar" }


示例2:

//合并 defaults 和 options, 不修改 defaults。
var empty = {};
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var settings = jQuery.extend(empty, defaults, options);

//结果:
settings == { validate: true, limit: 5, name: "bar" }
empty == { validate: true, limit: 5, name: "bar" }


这个重载的方法,我们一般用来在编写插件时用自定义插件参数去覆盖插件的默认参数。

自执行的匿名函数/闭包

什么是自执行的匿名函数?

它是指形如这样的函数: (function {// code})();

疑问 为什么(function {// code})();可以被执行, 而function {// code}();却会报错?

分析:

(1). 首先, 要清楚两者的区别: (function {// code})是表达式, function {// code}是函数声明.

(2). 其次, js”预编译”的特点: js在”预编译”阶段, 会解释函数声明, 但却会忽略表式.

(3). 当js执行到function() {//code}();时, 由于function() {//code}在”预编译”阶段已经被解释过, js会跳过function(){//code}, 试图去执行();, 故会报错;当js执行到(function {// code})();时, 由于(function {// code})是表达式, js会去对它求解得到返回值, 由于返回值是一 个函数, 故而遇到();时, 便会被执行.

另外, 函数转换为表达式的方法并不一定要靠分组操作符(),我们还可以用void操作符,~操作符,!操作符……

例如:

bootstrap 框架中的插件写法:

!function($){

  //do something;

}(jQuery);

//上面和下面这个是一回事。

(function($){

  //do something;

})(jQuery);


匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。

例如:

var a=1;

(function()(){

    var a=100;

  })();

alert(a);  // 弹出 1


更多 闭包和匿名函数 请关注后续的整理发布。

实例分析

1、 类级别的插件开发

1.1 增加一个新的全局函数

jQuery.foo = function() {
alert('This is a test. This is only a test.');
};


1.2 增加多个全局函数

jQuery.foo = function() {
alert('This is a test. This is only a test.');
};
jQuery.bar = function(param) {
alert('This function takes a parameter, which is "' + param + '".');
};
//调用时和一个函数的一样的:jQuery.foo();jQuery.bar();或者$.foo();$.bar('bar');


1.3 使用jQuery.extend(object)

jQuery.extend({
foo: function() {
alert('This is a test. This is only a test.');
},
bar: function(param) {
alert('This function takes a parameter, which is "' + param +'".');
}
});


1.4 使用命名空间

虽然在jQuery命名空间中,我们禁止使用了大量的javaScript函数名和变量名。但是仍然不可避免某些函数或变量名将于其他jQuery插件冲突,因此我们习惯将一些方法封装到另一个自定义的命名空间。

jQuery.myPlugin = {
foo:function() {
alert('This is a test. This is only a test.');
},
bar:function(param) {
alert('This function takes a parameter, which is "' + param + '".');
}
};
//采用命名空间的函数仍然是全局函数,调用时采用的方法:
$.myPlugin.foo();
$.myPlugin.bar('baz');


通过这个技巧(使用独立的插件名),我们可以避免命名空间内函数的冲突。

2、对象级别的插件开发

//形式一
(function($){
$.fn.extend({
pluginName:function(opt,callback){
...
}
})
})(jQuery);
//形式二
(function($) {
$.fn.pluginName = function() {
...
};
})(jQuery);


上面定义了一个jQuery函数,形参是
$
,函数定义完成之后,把jQuery这个实参传递进去.立即调用执行。这样的好处是,我们在写jQuery插件时,也可以使用
$
这个别名,而不会与prototype引起冲突.

2.1 在jQuery名称空间下申明一个名字

这是一个单一插件的脚本。如果你的脚本中包含多个插件,或者互逆的插件(例如:
$.fn.doSomething()
$.fn.undoSomething()
),那么你需要声明多个函数名字。但是,通常当我们编写一个插件时,力求仅使用一个名字来包含它的所有内容。我们的示例插件命名为“highlight“

$.fn.hilight = function() {
// ...
};
//调用:
$('#myDiv').hilight();


但是如果我们需要分解我们的实现代码为多个函数该怎么办?有很多原因:设计上的需要;这样做更容易或更易读的实现;而且这样更符合面向对象。 这真是一个麻烦事,把功能实现分解成多个函数而不增加多余的命名空间。出于认识到和利用函数是javascript中最基本的类对象,我们可以这样做。就像其他对象一样,函数可以被指定为属性。因此我们已经声明“hilight”为jQuery的属性对象,任何其他的属性或者函数我们需要暴露出来的,都可以在”hilight” 函数中被声明属性。稍后继续。

2.2 接受options参数以控制插件的行为

让我们为我们的插件添加功能指定前景色和背景色的功能。我们也许会让选项像一个options对象传递给插件函数。例如:

// plugin definition
$.fn.hilight = function(options) {
var defaults = {
foreground: 'red',
background: 'yellow'
};
// Extend our default options with those provided.
var opts = $.extend(defaults, options);
// Our plugin implementation code goes here.
};
我们的插件可以这样被调用:
$('#myDiv').hilight({
foreground: 'blue'
});


2.3 暴露插件的默认设置

我们应该对上面代码的一种改进是暴露插件的默认设置。这对于让插件的使用者更容易用较少的代码覆盖和修改插件。接下来我们开始利用函数对象。

// plugin definition
$.fn.hilight = function(options) {
// Extend our default options with those provided.
// Note that the first arg to extend is an empty object -
// this is to keep from overriding our "defaults" object.
var opts = $.extend({}, $.fn.hilight.defaults, options);
// Our plugin implementation code goes here.
};
// plugin defaults - added as a property on our plugin function
$.fn.hilight.defaults = {
foreground: 'red',
background: 'yellow'
};


现在使用者可以包含像这样的一行在他们的脚本里:

这个只需要调用一次,且不一定要在ready块中调用

$.fn.hilight.defaults.foreground = 'blue';


接下来我们可以像这样使用插件的方法,结果它设置蓝色的前景色:

$('#myDiv').hilight();


如你所见,我们允许使用者写一行代码在插件的默认前景色。而且使用者仍然在需要的时候可以有选择的覆盖这些新的默认值:

// 覆盖插件缺省的背景颜色
$.fn.hilight.defaults.foreground = 'blue';
// 使用一个新的缺省设置调用插件
$('.hilightDiv').hilight();
// 通过传递配置参数给插件方法来覆盖缺省设置
$('#green').hilight({
foreground: 'green'
});


2.4 适当暴露一些公共函数

这段将会一步一步对前面那段代码通过有意思的方法扩展你的插件(同时让其他人扩展你的插件)。例如,我们插件的实现里面可以定义一个名叫”format”的函数来格式化高亮文本。我们的插件现在看起来像这样,默认的format方法的实现部分在hiligth函数下面。

// plugin definition
$.fn.hilight = function(options) {
// iterate and reformat each matched element
return this.each(function() {
var $this = $(this);
// ...
var markup = $this.html();
// call our format function
markup = $.fn.hilight.format(markup);
$this.html(markup);
});
};
// define our format function
$.fn.hilight.format = function(txt) {
return '<strong>' + txt + '</strong>';
};


我们很容易的支持options对象中的其他的属性通过允许一个回调函数来覆盖默认的设置。这是另外一个出色的方法来修改你的插件。这里展示的技巧是进一步有效的暴露format函数进而让他能被重新定义。通过这技巧,是其他人能够传递他们自己设置来覆盖你的插件,换句话说,这样其他人也能够为你的插件写插件。

考虑到这个篇文章中我们建立的无用的插件,你也许想知道究竟什么时候这些会有用。一个真实的例子是Cycle插件.这个Cycle插件是一个滑动显示插件,他能支持许多内部变换作用到滚动,滑动,渐变消失等。但是实际上,没有办法定义也许会应用到滑动变化上每种类型的效果。那是这种扩展性有用的地方。 Cycle插件对使用者暴露”transitions”对象,使他们添加自己变换定义。插件中定义就像这样:

$.fn.cycle.transitions = {

// …

};

这个技巧使其他人能定义和传递变换设置到Cycle插件。

2.5 保持私有函数的私有性

这种技巧暴露你插件一部分来被覆盖是非常强大的。但是你需要仔细思考你实现中暴露的部分。一但被暴露,你需要在头脑中保持任何对于参数或者语义的改动也许会破坏向后的兼容性。一个通理是,如果你不能肯定是否暴露特定的函数,那么你也许不需要那样做。

那么我们怎么定义更多的函数而不搅乱命名空间也不暴露实现呢?这就是闭包的功能。为了演示,我们将会添加另外一个“debug”函数到我们的插件中。这个 debug函数将为输出被选中的元素格式到firebug控制台。为了创建一个闭包,我们将包装整个插件定义在一个函数中。

(function($) {
// plugin definition
$.fn.hilight = function(options) {
debug(this);
// ...
};
// private function for debugging
function debug($obj) {
if (window.console && window.console.log)
window.console.log('hilight selection count: ' + $obj.size());
};
//  ...
})(jQuery);


我们的“debug”方法不能从外部闭包进入,因此对于我们的实现是私有的。

2.6 支持Metadata插件

在你正在写的插件的基础上,添加对Metadata插件的支持能使他更强大。个人来说,我喜欢这个Metadata插件,因为它让你使用不多的”markup”覆盖插件的选项(这非常有用当创建例子时)。而且支持它非常简单。更新:注释中有一点优化建议。

$.fn.hilight = function(options) {
// ...
// build main options before element iteration
var opts = $.extend({}, $.fn.hilight.defaults, options);
return this.each(function() {
var $this = $(this);
// build element specific options
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
//...    


这些变动行做了一些事情:它是测试Metadata插件是否被安装如果它被安装了,它能扩展我们的options对象通过抽取元数据这行作为最后一个参数添加到JQuery.extend,那么它将会覆盖任何其它选项设置。现在我们能从”markup”处驱动行为,如果我们选择了“markup”:

调用的时候可以这样写: jQuery.foo(); 或 $.foo();

<!--  markup  -->
<div class="hilight { background: 'red', foreground: 'white' }">
Have a nice day!
</div>
<div class="hilight { foreground: 'orange' }">
Have a nice day!
</div>
<div class="hilight { background: 'green' }">
Have a nice day!
</div>
现在我们能高亮哪些div仅使用一行脚本:
$('.hilight').hilight();


2.7 整合

// 创建一个闭包
(function($) {
// 插件的定义
$.fn.hilight = function(options) {
debug(this);
// build main options before element iteration
var opts = $.extend({}, $.fn.hilight.defaults, options);
// iterate and reformat each matched element
return this.each(function() {
$this = $(this);
// build element specific options
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
// update element styles
$this.css({
backgroundColor: o.background,
color: o.foreground
});
var markup = $this.html();
// call our format function
markup = $.fn.hilight.format(markup);
$this.html(markup);
});
};
// 私有函数:debugging
function debug($obj) {
if (window.console && window.console.log)
window.console.log('hilight selection count: ' + $obj.size());
};
// 定义暴露format函数
$.fn.hilight.format = function(txt) {
return '<strong>' + txt + '</strong>';
};
// 插件的defaults
$.fn.hilight.defaults = {
foreground: 'red',
background: 'yellow'
};
// 闭包结束
})(jQuery);


3、选择器插件开发

jQuery以其强大的选择器著称,那么jQuery的选择器的工作原理是什么呢?

jQuery的选择解析器首先会使用一组正则表达式来解析选择器,然后针对解析出的每一个选择符执行一个函数,称为选择函数。最后根据这个选择函数的返回值为true还是false来决定是否保留这个元素,这样就可以找到匹配的元素节点。

如$(“div:gl(1)”),该选择器首先会获取所有的
元素,然后隐式地遍历这些元素,并逐个将这些元素作为参数,连同括号里的“1”等一些参数一起传递给gt对应的选择器函数进行判断。如果返回true则保留,否则不保留,这样得到的结果就是一个符合要求的元素的集合。

选择器的函数一共接受3个参数,形式如下:

function (a,i,m){
//...
}


第一个参数为a,指的是当前遍历到的DOM元素。

第二个参数为i,指的是当前遍历到的DOM元素的索引值,从0开始。

第三个参数是m,它是由jQuery正则解析引擎进一步解析后的产物,是一个数组:其中最重要的一个是m3,在$(“div:gl(1)”)中即为括号里的数字“1”。

在jQuery中已经有lt,gt和eq选择器,因此这里写一个介于两者之间(between)的选择器。

功能:选择索引值为a到b之间(a

;(function($){
$.extend($.expr[":"],{
between:function(a,i,m){
//插件代码
}
});
})(jQuery);


思路:在上面的三个参数中,m3为”a,b”的形式,因此把m3用”,”分隔,然后跟索引值i进行对比,如果i在m3表示的范围之间就返回true,否则为false

完整代码:

;(function($){
$.extend($.expr[":"],{
between:function(a,i,m){
var temp=m[3].split(",");
return +temp[0]<i&&i<+temp[1];
}
});
})(jQuery);


注:这里用+temp[0]、+temp1把字符串形式的数字转换成了数字

编写jQuery插件的一些Tips

  1、jQuery插件的文件名推荐命名为jquery.[插件名].js,以免和其他JS库插件混淆。

  2、所有的对象方法都应当附加到jQuery.fn对象上,而所有的全局函数都应当附加到jQuery对象本身。

  3、在插件头部加上一个分号,以免他人的不规范代码给插件带来影响。

  

  4、所有的方法或函数插件,都应当以分号结尾,以免压缩时出现问题。

  

  5、除非插件需要返回的是一些需要获取的变量,插件应该返回一个jQuery对象,以保证插件的可链式操作。

  6、利于jQuery.extend()方法设置插件方法的默认参数,增加插件的可用性。

举个栗子,写个高亮的jQuery插件

1、定义一个闭包去,防止插件“污染”

//闭包限定命名空间
;(function($){

})(jQuery);


2、jQuery.fn.extend(Object)扩展jQuery方法,制作插件

;(function($){
$.fn.extend({
//do something
});
})(jQuery);


3、设置插件默认参数,实现插件功能

;(function($){
$.fn.extend({
"hightLight":function(options) {
var opts = $.extend({}, defaults, options); //使用jQuery.extend()方法覆盖插件默认参数
//当调用highLight()插件的是一个集合的时候,要遍历所有对象dom
this.each(function(){ //这里的this就是jQuery对象
var $this = $(this); //获取当前循环dom的jQuery对象
$this.css({
background:opts.background,
color:opts.foreground
})
})
}
});
//默认参数
var defaults = {
background:"yellow";
foreground:"red";
}
})(jQuery);

//调用代码如下:

$(function(){
$("p").hightLight();
});


这里只能 直接调用,不能链式调用。我们知道jQuey是可以链式调用的,就是可以在一个jQuery对象上调用多个方法,如:

$('#id').css({marginTop:'100px'}).addAttr("title","测试");


但是我们上面的插件,就不能这样链式调用了。比如:

$("p").highLight().css({marginTop:'100px'});
//报错,找不到css方法 。

原因在与我的自定义插件在完成功能后,没有将 jQuery对象给返回出来。接下来,return jQuery对象,让我们的插件也支持链式调用。(其实很简单,就是执行完我们插件代码的时候将jQuery对像return 出来,和上面的代码没啥区别。)

//闭包限定命名空间
(function ($) {
$.fn.extend({
"highLight": function (options) {
//检测用户传进来的参数是否合法
if (!isValid(options))
return this;
var opts = $.extend({}, defaluts, options); //使用jQuery.extend 覆盖插件默认参数
return this.each(function () {  //这里的this 就是 jQuery对象。这里return 为了支持链式调用
//遍历所有的要高亮的dom,当调用 highLight()插件的是一个集合的时候。
var $this = $(this); //获取当前dom 的 jQuery对象,这里的this是当前循环的dom
//根据参数来设置 dom的样式
$this.css({
backgroundColor: opts.background,
color: opts.foreground
});
//格式化高亮文本
var markup = $this.html();
markup = $.fn.highLight.format(markup);
$this.html(markup);
});

}
});
//默认参数
var defaluts = {
foreground: 'red',
background: 'yellow'
};
//公共的格式化方法. 默认是加粗,用户可以通过覆盖该方法达到不同的格式化效果。
$.fn.highLight.format = function (str) {
return "<strong>" + str + "</strong>";
}
//私有方法,检测参数是否合法
function isValid(options) {
return !options || (options && typeof options === "object") ? true : false;
}
})(window.jQuery);

//调用
//调用者覆盖插件暴露的共公方法
$.fn.highLight.format = function (txt) {
return "<em>" + txt + "</em>"
}
$(function () {
$("p").highLight({ foreground: 'orange', background: '#ccc' }); //调用自定义 高亮插件
});


本文整理自:

Joey的博客

jQuery插件开发全解析

jQuery插件原来如此简单
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jquery