jquery编写插件的方法
开发Jquery插件
转自csdn:blog.csdn.net/manbufenglin/article/details/5017350
本章,我们简绍 几种不同插件的开发方法,有简单有复杂。首先,从添加新的全局函数的插件开始,然后再逐步讨论各种形式的jquery对象方法。此外,还将讨论以新表达式来扩展jquery选择符引擎,最后在简绍怎样把插件共享给其他开发人员。
11.1 添加新的全局函数
所谓全局函数,就是jquery对象的方法,但从实践的角度来说,它们是位于jquery命名空间内部的函数。例如$.ajax()所作的一切可以通过名为ajax()的常规全局函数来实现,但这种方式(通过$或jquery直接调用方法名)会造成函数命名冲突的问题。解决方法名冲突的方法是,通过将函数放入jquery的不同命名空间中去。
要向jquery命名空间中添加一个函数,只需要将这个函数指定为jquery对象的一个属性:
- jQuery.globalFunction = function() {
- alert('this is a test. Only a test.');
- };
于是我们就可以在使用这个插件的任何地方,编写如下代码使用它:
jquery.globalFunction();
或通过别名$写成:
$.globalFunction();
11.1.1 添加多个函数
如果想在同一个js文件中提供多个全局函数,可以独立地声明它们:
- jQuery.functionOne = function() {
- return alert('function first!');
- };
- jQuery.functionTwo = function() {
- return alert('function second!');
- };
- jQuery.functionThree = function() {
- return alert('function third!');
- };
调用它们:
- $.functionOne();
- $.functionTwo();
- $.functionThree();
还可以使用$.extend()函数使函数的定义更清晰:
- jQuery.extend({
- functionOne: function() {
- return alert('function first!');
- },
- functionTwo: function() {
- return alert('function second!');
- },
- functionThree: function() {
- return alert('function third!');
- }
- });
以上代码会得到同样的结果。
为避免方法名冲突,最好把属于一个插件的所有全局函数都封装到一个对象中去:
- jQuery.myPluginName = {
- functionOne: function() {
- return alert('function first!');
- },
- functionTwo: function() {
- return alert('function second!');
- },
- functionThree: function() {
- return alert('function third!');
- }
- };
调用时如下:
- $.myPluginName.functionOne();
- $.myPluginName.functionTwo();
- $.myPluginName.functionThree();
11.1.2 插件实用方法
核心的jquery库提供了许多全局函数都是实用方法。即这些方法为频繁执行任务提供了快捷方式。数组处理函数$.each()、$.map()、$.grep()都是这方面的例子。下面通过添加一个新的$.sum()方法,来示范如何创建这种实用方法。
这个方法通过接受一个数组参数,并将数组中的值累加起来,返回结果。代码如下:
- jQuery.sum = function(array) {
- var total = 0;
- jQuery.each(array, function(index, value) {
- total += value;
- });
- return total;
- };
为了测试方法,写一个页面,html内容如下:
- <body>
- <p>Array contents:</p>
- <ul id="array-contents"></ul>
- <p>Array sum:</p>
- <div id="array-sum"></div>
- </body>
接着在编写一段脚本,把返回值添加到标签array-sum上面去:
- $(document).ready(function() {
- var myArray = [52, 97, 0.5, -22];
- $.each(myArray, function(index, value) {
- $('#array-contents').append('<li>' + value + '</li>');
- });
- $('#array-sum').append($.sum(myArray));
- });
continuing...
11.2 添加jquery 对象方法
jquery内置功能大部分都是通过其对象的方法提供的,而这些方法也是插件之所以诱人的关键。当函数需要操作DOM元素时,就是将函数创建为jquery对象方法的好机会。
添加实例方法与添加全局方法类似,但扩展的是jquery.fn(jquery.prototype的别名)对象:
jquery.fn.myMethod=function(){
alter('Nothing happen!');
}
然后就可以在使用任何选择符表达式之后调用这个方法了:
$('div.myclassName').myMehtod();
我们前面提到“当函数需要操作DOM元素时,就是将函数创建为jquery对象方法的好机会”,一个合理的实例方法应包含对它的环境的操作。
11.2.1 对象方法的环境
在任何插件方法内部,关键字this引用的都是当前的jquery对象。因而,可以在this上面调用任何内置的jquery方法,或提取它包含的DOM节点并操作该节点:
- jQuery.fn.showAlter = function() {
- alter('You selected '+this.length+' elements.');
- };
为了确定如何才能利用对象环境,下面我们来编写个小插件,用来操作匹配元素的类。
- jQuery.fn.swapClass = function(class1, class2) {
- return this.each(function() { //this引用的是一个jquery对象
- var $element = jQuery(this);//this引用的是一个DOM元素
- if ($element.hasClass(class1)) {
- $element.removeClass(class1).addClass(class2);
- }
- else if ($element.hasClass(class2)) {
- $element.removeClass(class2).addClass(class1);
- }
- });
- };
为了测试这段代码是否有效,需要以下html:
- <body>
- <ul>
- <li>Lorem ipsum dolor sit amet</li>
- <li class="this">Consectetur adipisicing elit</li>
- <li>Sed do eiusmod tempor incididunt ut labore</li>
- <li class="that">Magna aliqua</li>
- <li class="this">Ut enim ad minim veniam</li>
- <li>Quis nostrud exercitation ullamco</li>
- <li>Laboris nisi ut aliquip ex ea commodo</li>
- <li class="that">Duis aute irure dolor</li>
- </ul>
- <input type="button" value="Swap classes" id="swap" />
- </body>
样式表css 如下:
- body {
- font: 62.5% Arial, Verdana, sans-serif;
- }
- ul {
- margin: 1em 0;
- padding: 0;
- }
- li {
- font-size: 1.1em;
- margin: 0.2em 1em;
- padding: 0;
- }
- .this {
- font-weight: bold;
- }
- .that {
- font-style: italic;
- }
方法swapClass中需要解释的几个地方:
-
隐式迭代:要在匹配多个元素的情况下保证行为正确,最简单的方式就是始终在方法的环境上调用.each()方法,这就会执行隐式迭代,而隐式迭代对于维护插件与内置方法的一致性是至关重要的。
-
方法连缀:在定义对象方法时,我们必须在所有插件方法中返回一个jquery对象(除非相应的方法明显用于取得不同的信息),以便于能够正常使用连缀行为。如下面所示:
- $(document).ready(function() {
- $('#swap').click(function() {
- $('li')
- .swapClass('this', 'that')
- .css('text-decoration', 'underline');//连缀方法
- return false;
- });
- });
11.3 Dom遍历方法
在某些情况下,我们定义的插件方法可能会改变jquery对象引用的DOM元素。
比如,我们想要添加一个查找匹配元素的祖父级元素的DOM遍历方法:
- jQuery.fn.grandparent = function() {
- var grandparents = [];
- this.each(function() {
- grandparents.push(this.parentNode.parentNode);
- });
- grandparents = jQuery.unique(grandparents);
- return this.setArray(grandparents);
- };
解释下这段代码:创建一个新的grandparents数组,并通过迭代将当前juqery对象引用的全部元素填充这个数组。然后调用.unique()方法去掉填充后的数组中重复的元素;最后,jquery内置的方法.setArray()把这组元素转换成新数组。
为了测试这个方法,定义一个深度嵌套的<div>结构:
- <div>
- Deserunt mollit anim id est laborum</div>
- <div>
- Ut enim ad minim veniam
- <div>
- Quis nostrud exercitation
- <div>
- Ullamco laboris nisi
- <div>
- Ut aliquip ex ea</div>
- <div class="target">
- Commodo consequat
- <div>
- Lorem ipsum dolor sit amet</div>
- </div>
- </div>
- <div>
- Duis aute irure dolor</div>
- <div>
- In reprehenderit
- <div>
- In voluptate</div>
- <div>
- Velit esse
- <div>
- Cillum dolore</div>
- <div class="target">
- Fugiat nulla pariatur</div>
- </div>
- <div>
- Excepteur sint occaecat cupidatat</div>
- </div>
- </div>
- <div>
- Non proident</div>
- </div>
- <div>
- Sunt in culpa qui officia</div>
粗体标示目标元素(<div class="target">
)。使用我们定义的方法,就可以取得目标元素的祖父级的元素了。执行调用方法如下:
- $(document).ready(function() {
- var $target = $('.target');
- $target.grandparent().addClass('highlight');
- $target.hide();
- });
然而,我们定义的grandparent方法是有破坏性的。因为调用方法作用是突出显示祖父级元素,然后再隐藏自身,而实际的效果是隐藏了祖父级别的元素,而非自身。
为了避免这种情况,需要改变我们定义的方法,借助jquery在内部为每个对象维护的栈,可以做到这一点。
- jQuery.fn.grandparent = function() {
- var grandparents = [];
- this.each(function() {
- grandparents.push(this.parentNode.parentNode);
- });
- grandparents = jQuery.unique(grandparents);
- return this.pushStack(grandparents);//会创建一个新jquery对象,而非修改原有对象
- };
这样,$target就没有被修改,最初的目标对象将被代码隐藏起来。在调用此方法时,还可以连缀方法。如:
$(document).ready(function() {
$('.target').grandparent().andSelf().addClass('highlight');
});
11.4 添加新的简写方法
jquery库必须在方便和复杂之间维持一个微妙的平衡。添加到库中的每个方法都有助于开发者简化某些代码的编写,但也会增加基础代码的整体大小并可能影响性能。基于这点考虑,内置功能的许多简写方法都移交到插件中实现,以便开发者可以挑出对某个项目有用的方法,并省略那些无用的方法。
当我们发现自己在代码中需要多次重复使用某个方法时,可能会想到为该方法创建一种简写的形式。如,假设我们利用内置的“滑动”和“淡化”技术频繁为页面元素添加动画效果。这两个效果放到一起意味着同时变换元素的高度和不透明度,而使用.animate()方法可以简化这两个操作。
- jQuery.fn.slideFadeOut = function() {
- return this.animate({
- height: 'hide',
- opacity: 'hide'
- });
- };
- //淡入
- jQuery.fn.slideFadeIn = function() {
- return this.animate({
- height: 'show',
- opacity: 'show'
- });
- };
- //折叠(淡入、淡出)
- jQuery.fn.slideFadeToggle = function() {
- return this.animate({
- height: 'toggle',
- opacity: 'toggle'
- });
- };
出于完整性考虑,我们的新方法也应该支持与内置的简写方法相同的参数。具体来说,应该像.fadeIn()方法一样那个自定义速度和回调函数。所以,可以进一步改写代码:
- jQuery.fn.slideFadeOut = function(speed, callback) {
- return this.animate({
- height: 'hide',
- opacity: 'hide'
- }, speed, callback);
- };
- jQuery.fn.slideFadeIn = function(speed, callback) {
- return this.animate({
- height: 'show',
- opacity: 'show'
- }, speed, callback);
- };
- jQuery.fn.slideFadeToggle = function(speed, callback) {
- return this.animate({
- height: 'toggle',
- opacity: 'toggle'
- }, speed, callback);
- };
这样,我们就有了与内置的简写方法具有类似功能的自定义的简写方法。为了演示这个方法,需要一个简单的html页面:
- <div>
- <p>
- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
- incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
- exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
- irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
- pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
- deserunt mollit anim id est laborum.</p>
- <div class="controls">
- <asp:Button ID="out" runat="server" Text="Slide and fade out" />
- <%--<input type="button" value="Slide and fade out" id="out" />--%>
- <input type="button" value="Slide and fade in" id="in" />
- <input type="button" value="Toggle" id="toggle" />
- </div>
在按钮单击时,脚本会调用我们定义的新方法:
- /// <reference path="../../scripts/jquery-1.3.2-vsdoc2.js" />
- /// <reference path="jquery.solidFade.js" />
- $(function() {
- $('#out').click(function() {
- $('p').slideFadeOut('slow');
- return false;
- });
- $('#in').click(function() {
- $('p').slideFadeIn('slow');
- return false;
- });
- $('#toggle').click(function() {
- $('p').slideFadeToggle('slow');
- return false;
- });
- });
而效果也和我们想象的一样。
continuing...
11.5 方法的参数
下面介绍几种管理方法参数的技巧。例子:为文本块添加投影添加插件方法。
文本块代码如下:
- <body>
- <h1>
- The quick brown fox jumps over the lazy dog.</h1>
- </body>
要实现此文本块(标题)的投影效果,需要编写一小段代码(方法),实现的思路是:对于每个调用此方法的元素,都要复制该元素一定数量的副本,调整每个副本的不透明度,再通过绝对定位的方式,以原有元素为基准按照不同的偏移量定位这些副本。
实现代码如下:
- jQuery.fn.shadow = function() {
- return this.each(function() {
- var $originalElement = $(this);
- for (var i = 0; i < 5; i++) {
- $originalElement
- .clone()
- .css({
- position: 'absolute',//绝对定位方式,让每个副本基于上一个元素按照不同的偏移量定位
- left: $originalElement.offset().left + i,
- top: $originalElement.offset().top + i,
- margin: 0,
- zIndex: -1,
- opacity: 0.1
- })
- .appendTo('body'); //加入到body标签中
- }
- });
- };
调用此插件方法很简单:
$(document).ready(function() {
$('h1').shadow();
});
11.5.1 为方法添加简单参数
为了让用户使用此方法时,能够修改副本的相对位移、透明度等,我们需要把这些值定义为方法接受的参数列表。修改后的方法如下:
- //step2(接受3个参数。缺点:参数不太明确,易搞混)
- jQuery.fn.shadow = function(slice, opacity, zIndex) {
- return this.each(function() {
- var $originalElement = $(this);
- for (var i = 0; i < slice; i++) {
- $originalElement
- .clone()
- .css({
- position: 'absolute', //绝对定位方式,让每个副本基于上一个元素按照不同的偏移量定位
- left: $originalElement.offset().left + i,
- top: $originalElement.offset().top + i,
- margin: 0,
- zIndex: zIndex,
- opacity: opacity
- })
- .appendTo('body'); //加入到body标签中
- }
- });
- };
调用此对象方法:
$(document).ready(function() {
$('h1').shadow(10, 0.05, -2);
});
但这个对象方法还不够理想,这3个参数容易搞混,它们次序没有任何规则可循。
11.5.2 参数映射
作为一种向用户公开选项的方式,映射显然要比参数列表更加友好。映射会为每个参数提供一个更有意义的标签,同时也让参数次序变得无关紧要,而且,只要有可能通过插件来模仿jquery API,就应该使用映射来提高一致性和易用性。
- //step3(参数映射)
- jQuery.fn.shadow = function(opts) {
- return this.each(function() {
- var $originalElement = jQuery(this);
- for (var i = 0; i < opts.slices; i++) {
- $originalElement
- .clone()
- .css({
- position: 'absolute',
- left: $originalElement.offset().left + i,
- top: $originalElement.offset().top + i,
- margin: 0,
- zIndex: opts.zIndex,
- opacity: opts.opacity
- })
- .appendTo('body');
- }
- });
- };
调用这个方法需要传递一个值映射,而不是独立的各个参数了。
$(document).ready(function() {
$('h1').shadow({
slices: 8,
opacity: 0.1,
zIndex: 1
});
});
这样,只要扫一眼调用方法的代码,就知道每个参数的作用。
11.5.3 默认参数值
随着参数的增多,在影射中始终指定每个参数不是必须的。此时,一组合理的默认值可以增强插件接口的易用性。
- //step4(默认参数值)
- jQuery.fn.shadow = function(options) {
- var defaults = {
- slices: 10,
- opacity: 0.1,
- zIndex: -1
- };
- var opts = jQuery.extend(defaults, options);
- return this.each(function() {
- var $originalElement = jQuery(this);
- for (var i = 0; i < opts.slices; i++) {
- $originalElement
- .clone()
- .css({
- position: 'absolute',
- left: $originalElement.offset().left + i,
- top: $originalElement.offset().top + i,
- margin: 0,
- zIndex: opts.zIndex,
- opacity: opts.opacity
- })
- .appendTo('body');
- }
- });
- };
在这个方法中,我们定义了一个新的映射defaults,实用函数.extend()可以用接受的选项映射参数来覆盖defaults中的对应项,并保持选项映射中未指定的默认项不变。
调用方法如下:
$(document).ready(function() {
$('h1').shadow({
opacity: 0.05 //仅修改透明度值,未指定的参数值为预先定义的默认项值
});
});
$.extend()方法可以接受null值,在用户可以接受所有默认参数时,我们的方法可以直接执行而不会出错:
$(document).ready(function() {
$('h1').shadow();
});
11.5.4 回调函数
要在方法中使用回调函数,需要接受一个函数对象作为参数,然后在方法中适当的位置上调用该函数。例如:扩展前面定义的文本投影方法,让用户自定义投影相对于文本的位置。
- //step5(回调函数)
- jQuery.fn.shadow = function(options) {
- var defaluts = {
- slices: 5,
- opacity: 0.1,
- zIndex: -1,
- sliceOffset: function(i) {
- return { x: i, y: i };
- }
- };
- var opts = jQuery.extend(defaluts, options);
- return this.each(function() {
- var $originalElement = jQuery(this);
- for (var i = 0; i < opts.slices; i++) {
- var offset = opts.sliceOffset(i);
- $originalElement
- .clone()
- .css({
- position: 'absolute',
- left: $originalElement.offset().left + offset.x,
- top: $originalElement.offset().top + offset.y,
- margin: 0,
- zIndex: opts.zIndex,
- opacity: opts.opacity
- })
- .appendTo('body');
- }
- });
- };
投影的每个“切片”相对于原始文本都有不同的偏移量。现在,偏移量根据函数sliceOffset()计算出来,用户可以自定义此函数。调用此方法如下:
$(document).ready(function() {
$('h1').shadow({
sliceOffset: function(i) {
return {x: -i, y: -2*i};
}//sliceOffset函数
});//映射参数结束,投影方法结束
});
11.5.5 可定制的默认值
如果有脚本多次调用我们的插件,每次调用都要传递一组不同于默认值的参数,那么定制默认值就显得有必要,这样调用时可以减少很多的代码的编写。
要支持默认值的可定制,需要把它们从方法定义中移出来,放到外部代码可以访问的地方:
- //step6 (可定制的默认值)
- jQuery.fn.shadow = function(options) {
- var opts = jQuery.extend({},
- jQuery.fn.shadow.defaults, options); //defaults不被修改,使空映射{}成为被修改的新对象
- return this.each(function() {
- var $originalElement = jQuery(this);
- for (var i = 0; i < opts.slices; i++) {
- var offset = opts.sliceOffset(i);
- $originalElement
- .clone()
- .css({
- position: 'absolute',
- left: $originalElement.offset().left + offset.x,
- top: $originalElement.offset().top + offset.y,
- margin: 0,
- zIndex: opts.zIndex,
- opacity: opts.opacity
- })
- .appendTo('body');
- }
- });
- };
- jQuery.fn.shadow.defaults = {
- slices: 5,
- opacity: 0.1,
- zIndex: -1,
- sliceOffset: function(i) {
- return { x: i, y: i };
- }
- };
由于现在所有对.shadow()的调用都要重用defaults映射,因此不能让$.extend()修改它,我们在此定义一个空映射({})作为$.extend()第一个个参数,让这个新对象作为被修改的目标。
于是,使用插件方法时就可以修改默认值了,修改之后的值可以被所有后续对.shadow()的调研共享,而且,在调用方法是仍然可以传递选项:
- $(document).ready(function() {
- $.fn.shadow.defaults.slices = 10;
- $('h1').shadow({
- sliceOffset: function(i) {
- return { x: -i, y: i };
- }
- });
- });
11.6 自定义选择符表达式
jquery内置的组件也可以扩展,与添加新方法不同,我们可以自定义现有的组件。比如一个常见的需求就是自定义jquery提供的选择符表达式,以便得到更高级的选择符。
最简单的选择符 表达式是伪类,即以冒号开头的表达式,如:checked/:nth-child()等。
下面,我们自定义一个选择符表达式,:css()伪类,这个选择符表达式允许我们基于css属性的数字值查找元素。
- ///定义样式选择符表达式css
- jQuery.extend(jQuery.expr[':'], {
- 'css': function(element, index, matches, set) {
- var parts = /([/w-]+)/s*([<>=]+)/s*(/d+)/
- .exec(matches[3]); /*matches:包含解析当前选择符的正则表达式结果的数组。matches[3],通常是这个数组中有用的项。对
- :a(b)形式的选择符而言,matches[3]项包含着b,即圆括号中的文本*/
- //选择元素属性值
- var value = parseFloat(jQuery(element).css(parts[1]));
- var selectedValue = parseInt(parts[3]);
- switch (parts[2]) { //匹配比较符,返回bool值,为真时,选择该元素;否则放弃该元素
- case '<':
- return value < parseInt(parts[3]);
- case '<=':
- return value <= parseInt(parts[3]);
- case '=':
- return value == parseInt(parts[3]);
- case '>':
- return value > parseInt(parts[3]);
- case '>=':
- return value >= parseInt(parts[3]);
- }
- }
- });
需要解释的如下:
-
以上代码告诉jquery,css是一个可以在选择符表达式中前置冒号的有效字符串,当遇到该字符串时,应该调用给定的函数以确定当前元素是否应该包含在结果集中。
-
element:当前的DOM元素。大多数选择符都需要这个参数。
-
index:DOM元素在结果集中的索引。这个参数对:eq()和:lt()等选择符有用。
-
matches:包含解析当前选择符的正则表达式结果的数组。通常matches[3]是 这个数组中唯一有用的项。
-
set:到目前为止匹配的整个DOM元素集合。很少用。
我们可以通过一下文档演示它的用途:
- <div>
- Deserunt mollit anim id est laborum</div>
- <div>
- Ullamco</div>
- <div>
- Ut enim ad minim veniam laboris</div>
- <div>
- Quis nostrud exercitation consequat nisi</div>
- <div style="width:60px">
- Ut aliquip</div>
- <div>
- Commodo</div>
- <div>
- Lorem ipsum dolor sit amet ex ea</div>
使用新的选择符表达式,突出显示里表中文本较短的项就很容易了:
$(function() {
$('div:css(width < 100)').addClass('highlight');
});
参考《jquery基础教程》第二版