如何建立一个基本JQuery的插件

如何建立一个基本的插件

有时您但愿在整个代码中提供一些功能。例如,也许你想要一个单一的方法,你能够调用一个jQuery选择,对选择执行一系列的操做。在这种状况下,您可能须要编写一个插件。javascript

连接jQuery如何工做101:jQuery对象方法

在咱们编写本身的插件以前,首先要了解一下jQuery如何工做。看看这段代码:css

1
$( "a" ).css( "color", "red" );

这是一些很基础的jQuery代码,但你知道幕后发生了什么吗?不管什么时候使用该$函数来选择元素,它返回一个jQuery对象。这个对象包含了全部的你已经使用(方法.css().click()等)和全部适合你的选择要素。jQuery对象从对象中获取这些方法$.fn这个对象包含了全部的jQuery对象方法,若是咱们想编写本身的方法,它也须要包含这些方法。html

连接基本插件创做

假设咱们要建立一个插件,使一组检索到的元素中的文本变为绿色。咱们所要作的就是添加一个调用的函数greenify$.fn,这将是可用的,就像任何其余的jQuery对象的方法。java

1
2
3
4
$.fn.greenify = function() {
this.css( "color", "green" );
};
 
$( "a" ).greenify(); // Makes all the links green.

注意使用.css(),另外一种方法,咱们使用this,而不是$( this )这是由于咱们的greenify函数是与之相同的对象的一部分.css()node

连接连接

这是有效的,可是咱们须要作的几件事情就是让咱们的插件在现实生活中生存下去。当您将五个或六个操做连接到一个选择器上时,jQuery的功能之一就是连接。这是经过使全部jQuery对象方法再次返回原始的jQuery对象来实现的(有一些例外:.width()调用无参数返回所选元素的宽度,而且不可连接)。使咱们的插件方法可连接须要一行代码:jquery

1
2
3
4
6
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
}
 
$( "a" ).greenify().addClass( "greenified" );

连接保护$别名和添加范围

$变量是JavaScript库中很受欢迎,若是你正在使用jQuery的另外一个库,你将不得不做出的jQuery不使用$jQuery.noConflict()然而,这将会破坏咱们的插件,由于它是用函数$的别名假设来写jQuery为了与其余插件很好地工做,而且仍然使用jQuery的$别名,咱们须要把咱们全部的代码里面当即调用函数表达式,而后传递给函数jQuery,并命名参数$express

1
2
3
4
6
7
8
(function ( $ ) {
 
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
 
}( jQuery ));

此外,当即调用函数的主要目的是容许咱们拥有本身的私有变量。伪装咱们想要一个不一样的颜色绿色,咱们想把它存储在变量中。promise

1
2
3
4
6
7
8
9
10
(function ( $ ) {
 
var shade = "#556b2f";
 
$.fn.greenify = function() {
this.css( "color", shade );
return this;
};
 
}( jQuery ));

连接最小化插件足迹

编写插件时只需占用一个插槽就是很好的作法$.fn这样能够减小您的插件被覆盖的机会,而且插件将覆盖其余插件的机会。换句话说,这是坏的:闭包

1
2
3
4
6
7
8
9
10
11
(function( $ ) {
 
$.fn.openPopup = function() {
// Open popup code.
};
 
$.fn.closePopup = function() {
// Close popup code.
};
 
}( jQuery ));

有一个插槽会更好,并使用参数来控制一个插槽执行什么操做。app

1
2
3
4
6
7
8
9
10
11
12
13
14
15
(function( $ ) {
 
$.fn.popup = function( action ) {
 
if ( action === "open") {
// Open popup code.
}
 
if ( action === "close" ) {
// Close popup code.
}
 
};
 
}( jQuery ));

连接使用each()方法

您的典型jQuery对象将包含对任意数量的DOM元素的引用,这就是为何jQuery对象一般被称为集合。若是要对特定元素进行任何操做(例如获取数据属性,计算特定位置),则须要使用.each()循环元素。

1
2
3
4
6
7
$.fn.myNewPlugin = function() {
 
return this.each(function() {
// Do something to each element here.
});
 
};

请注意,咱们返回结果.each()而不是返回this既然.each()已是可连接的,它返回this,而后咱们返回。这是保持可连接性的一种更好的方法,而不是咱们迄今为止所作的一切。

连接接受选项

随着您的插件愈来愈复杂,最好经过接受选项来使您的插件可自定义。最简单的方法是这样作,特别是若是有不少选项,那就是一个对象文字。咱们更改咱们的greenify插件以接受一些选项。

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(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 ));

使用示例

1
2
3
$( "div" ).greenify({
color: "orange"
});

为默认值color#556b2f获取经过重写$.extend()为橙色。

连接在一块儿

这是一个使用咱们讨论的一些技巧的小插件的例子:

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
(function( $ ) {
 
$.fn.showLinkLocation = function() {
 
this.filter( "a" ).each(function() {
var link = $( this );
link.append( " (" + link.attr( "href" ) + ")" );
});
 
return this;
 
};
 
}( jQuery ));
 
// Usage example:
$( "a" ).showLinkLocation();

这个方便的插件遍历集合中的全部锚点,并将该href属性附加在括号中。

1
2
3
4
<!-- Before plugin is called: -->
<a href="page.html">Foo</a>
 
<!-- After plugin is called: -->
<a href="page.html">Foo (page.html)</a>

咱们的插件能够经过如下优化:

1
2
3
4
6
7
8
9
10
11
12
13
(function( $ ) {
 
$.fn.showLinkLocation = function() {
 
this.filter( "a" ).append(function() {
return " (" + this.href + ")";
});
 
return this;
 
};
 
}( jQuery ));

咱们使用该.append()方法的能力来接受回调,而且该回调的返回值将决定附加到集合中每一个元素的内容。还要注意,咱们没有使用该.attr()方法来检索href属性,由于本机DOM API能够方便地访问aptly named href属性。

高级插件概念

连接提供公共访问默认插件设置

咱们能够并且应该对上面的代码进行改进是公开默认的插件设置。这很重要,由于它使插件用户很容易用最小的代码覆盖/定制插件。这就是咱们开始利用函数对象的地方。

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
// Plugin definition.
$.fn.hilight = function( options ) {
 
// Extend our default options with those provided.
// Note that the first argument 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"
};

如今,用户能够在脚本中包含这样一行:

1
2
3
// This needs only be called once and does not
// have to be called from within a "ready" block
$.fn.hilight.defaults.foreground = "blue";

如今咱们能够像这样调用插件方法,它将使用蓝色的前景色:

1
$( "#myDiv" ).hilight();

您能够看到,咱们容许用户编写一行代码来更改插件的默认前景色。而用户仍然能够选择性地覆盖这个新的默认值:

1
2
3
4
6
7
8
9
10
11
12
13
14
// Override plugin default foreground color.
$.fn.hilight.defaults.foreground = "blue";
 
// ...
 
// Invoke plugin using new defaults.
$( ".hilightDiv" ).hilight();
 
// ...
 
// Override default by passing options to plugin method.
$( "#green" ).hilight({
foreground: "green"
});

连接提供公共访问次要功能做为适用

该项目与上一个项目紧密相关,是扩展插件(并容许其余扩展插件)的有趣方式。例如,咱们的插件的实现能够定义一个称为“格式”的功能,其格式化高亮文本。咱们的插件可能看起来像这样,默认实现的格式方法定义在hilight函数下面:

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Plugin definition.
$.fn.hilight = function( options ) {
 
// Iterate and reformat each matched element.
return this.each(function() {
 
var elem = $( this );
 
// ...
 
var markup = elem.html();
 
// Call our format function.
markup = $.fn.hilight.format( markup );
 
elem.html( markup );
 
});
 
};
 
// Define our format function.
$.fn.hilight.format = function( txt ) {
return "<strong>" + txt + "</strong>";
};

咱们能够轻松地支持选项对象上的另外一个属性,容许提供回调函数来覆盖默认格式。这是支持自定义插件的另外一个很好的方法。这里显示的技术经过实际暴露格式功能使其能够从新定义,从而进一步扩展。使用这种技术,其余人可能会发布本身的插件自定义覆盖 - 换句话说,这意味着其余人能够为插件编写插件。

考虑到咱们在本文中构建的简单的示例插件,您可能会想知道什么时候会有用。一个现实世界的例子是循环插件循环插件是一个幻灯片插件,它支持多种内置的转换效果 - 滚动,滑动,淡入淡出等。可是,实际上,没有办法定义可能但愿应用于幻灯片转换的每种类型的效果。这就是这种类型的可扩展性是有用的。循环插件公开了一个“transition”对象,用户能够添加本身的自定义转换定义。它在插件中定义以下:

1
2
3
4
$.fn.cycle.transitions = {
 
// ...
 
};

这种技术使得其余人能够定义和提供插件到循环插件的转换定义。

连接保持私人功能私有

暴露部分插件被覆盖的技术能够很是强大。可是,您须要仔细考虑实施的哪些部分才能公开。一旦暴露,您须要记住,调用参数或语义的任何更改可能会破坏向后兼容性。做为通常规则,若是您不肯定是否公开特定功能,那么您可能不该该。

那么,咱们如何定义更多的功能,而不会混淆命名空间,而不会暴露实现?这是关闭的工做。为了演示,咱们将在咱们的插件中添加另外一个函数叫作“debug”。调试功能将所选元素的数量记录到控制台。要建立一个闭包,咱们将整个插件定义包装在一个函数中(如jQuery创做指南中所详述的)。

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Create closure.
(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.length );
}
};
 
// ...
 
// End of closure.
 
})( jQuery );

咱们的“调试”方法没法从关闭的外部访问,所以对咱们的实现是私有的。

连接Bob和Sue

假设鲍勃建立了一个邪恶的新画廊插件(称为“超级画廊”),其中包含一幅图像列表,并使其可导航。鲍勃抛出了一些动画,使它更有趣。他试图使插件尽量的自定义,最终结果是这样的:

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
jQuery.fn.superGallery = function( options ) {
 
// Bob's default settings:
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
delay: "quite long",
getTextFromTitle: true,
getTextFromRel: false,
getTextFromAlt: false,
animateWidth: true,
animateOpacity: true,
animateHeight: true,
animationDuration: 500,
clickImgToGoToNext: true,
clickImgToGoToLast: false,
nextButtonText: "next",
previousButtonText: "previous",
nextButtonTextColor: "red",
previousButtonTextColor: "red"
};
 
var settings = $.extend( {}, defaults, options );
 
return this.each(function() {
// Plugin code would go here...
});
 
};

第一件事多是你的头脑(好的,也许不是第一个),这个插件必须有多大,以适应这样一个级别的定制。插件,若是不是虚构的,可能会比必要的大得多。只有这么多千字节的人会愿意花费!

如今,咱们的朋友鲍勃认为这一切都很好; 事实上,他对插件及其定制级别印象深入。他认为,全部的选择均可以使用更为通用的解决方案,能够在许多不一样的状况下使用。

苏,咱们的另外一个朋友,决定使用这个新的插件。她已经设置了全部的选项,如今有一个工做的方案坐在她面前。只有五分钟后,玩插件后,若是每张图片的宽度都以较慢的速度动画,她意识到画廊会更好看。她匆忙搜索Bob的文档,但没有发现animateWidthDuration选项!

连接你看到问题吗?

这不是真的关于您的插件有多少选项; 但它有什么选择!

鲍勃已经超过了顶峰。他提供的定制水平虽然可能高,但实际上至关低,特别是考虑到使用此插件时可能须要控制的全部可能的事情。鲍勃犯了错误提供了不少好笑的具体选择,使他的插件更难以自定义!

连接更好的模型

因此很明显:鲍勃须要一个新的定制模式,一个不放弃控制或抽出必要细节的模型。

鲍勃如此吸引这个高级简单性的缘由是,jQuery框架很是适合于这种观念。提供一个previousButtonTextColor选项是好的和简单的,但让咱们面对它,绝大多数的插件用户将要更多的控制!

如下是一些提示,可帮助您为插件建立更好的可定制选项:

连接不要建立特定于插件的语法

使用您的插件的开发人员不该该学习一种新的语言或术语,只是为了完成这项工做。

鲍勃认为他提供最大的定制与他的延迟选项(看上面)。他作的这样,他的插件能够指定四个不一样的延迟,“很短”,“很短”,“至关长”或“很长”:

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var delayDuration = 0;
 
switch ( settings.delay ) {
 
case "very short":
delayDuration = 100;
break;
 
case "quite short":
delayDuration = 200;
break;
 
case "quite long":
delayDuration = 300;
break;
 
case "very long":
delayDuration = 400;
break;
 
default:
delayDuration = 200;
 
}

这不只限制了人们的控制水平,并且占用了至关多的空间。十二行代码只是为了定义延迟时间有点多,你不以为吗?构建此选项的更好方法是让插件用户以数字的形式指定时间(以毫秒为单位),以便不须要处理该选项。

这里的关键不在于经过抽象来减小控制的程度。您的抽象,不管如何,能够像您想要的同样简单,但确保使用您的插件的人仍然会有这么多追捧的低级控制!(低级个人意思是非抽象的)

连接充分控制元素

若是您的插件建立要在DOM中使用的元素,那么为插件用户提供一些访问这些元素的方法是个好主意。有时这意味着给予某些元素ID或类。但请注意,您的插件不该该在内部依赖这些钩子:

执行不良

1
2
3
4
// Plugin code
$( "<div class='gallery-wrapper' />" ).appendTo( "body" );
 
$( ".gallery-wrapper" ).append( "..." );

为了容许用户访问甚至操纵该信息,您能够将其存储在包含插件设置的变量中。之前的代码的更好的实现以下所示:

1
2
3
4
6
7
// Retain an internal reference:
var wrapper = $( "<div />" )
.attr( settings.wrapperAttrs )
.appendTo( settings.container );
 
// Easy to reference later...
wrapper.append( "..." );

请注意,咱们建立了一个引用包装器的引用,而且咱们还调用该.attr()方法来向元素添加任何指定的属性。因此在咱们的设置中可能会像这样处理:

1
2
3
4
6
7
8
9
10
var defaults = {
wrapperAttrs : {
class: "gallery-wrapper"
},
// ... rest of settings ...
};
 
// We can use the extend method to merge options/settings as usual:
// But with the added first parameter of TRUE to signify a DEEP COPY:
var settings = $.extend( true, {}, defaults, options );

$ .extend()方法如今将经过全部嵌套对象递归给咱们设定值和经过选择二者的合并版本,给人传递的选项优先。

插件用户如今有权力指定该包装器元素的任何属性,所以若是它们须要任何CSS样式的钩子,那么他们能够很容易地添加一个类或更改ID的名称,而无需去挖掘插件源码。

可使用相同的模型来让用户定义CSS样式:

1
2
3
4
6
7
8
9
10
var defaults = {
wrapperCSS: {},
// ... rest of settings ...
};
 
// Later on in the plugin where we define the wrapper:
var wrapper = $( "<div />" )
.attr( settings.wrapperAttrs )
.css( settings.wrapperCSS ) // ** Set CSS!
.appendTo( settings.container );

您的插件可能有一个关联的样式表,开发人员能够添加CSS样式。即便在这种状况下,最好提供一些方便的方法来设置JavaScript中的样式,而无需使用选择器来获取元素。

连接提供回调功能

什么是回调? - 回调本质上是一个稍后调用的函数,一般由一个事件触发。它做为参数传递,一般是组件的发起调用,在这种状况下是一个jQuery插件。

若是您的插件由事件驱动,那么为每一个事件提供回调功能多是一个好主意。此外,您能够建立本身的自定义事件,而后为其提供回调。在这个gallery插件中添加一个“onImageShow”回调多是有意义的。

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var defaults = {
 
// We define an empty anonymous function so that
// we don't need to check its existence before calling it.
onImageShow : function() {},
 
// ... rest of settings ...
 
};
 
// Later on in the plugin:
 
nextButton.on( "click", showNextImage );
 
function showNextImage() {
 
// Returns reference to the next image node
var image = getNextImage();
 
// Stuff to show the image here...
 
// Here's the callback:
settings.onImageShow.call( image );
}

而不是经过传统方式(加括号)启动回调,咱们在上下文中调用它将image是对图像节点的引用。这意味着您能够经过this回调中关键字访问实际的图像节点

1
2
3
4
6
7
$( "ul.imgs li" ).superGallery({
onImageShow: function() {
$( this ).after( "<span>" + $( this ).attr( "longdesc" ) + "</span>" );
},
 
// ... other options ...
});

一样,您能够添加一个“onImageHide”回调函数和其余许多回调函数。回调点是给插件用户一个简单的方法来添加额外的功能,而不须要在源代码中挖掘。

连接记住,这是一个妥协

您的插件没法在每种状况下工做。一样地,若是您提供的控制方法不是不多或不多,则不会很是有用。因此,请记住,这将是一个妥协。您必须始终考虑的三件事情是:

  • 灵活性:您的插件能够处理多少状况?
  • 大小:插件的大小是否对应于其功能级别?也许你使用一个很是基本的工具提示插件,若是它是20k的大小?- 可能不会!
  • 性能:您的插件是否以任何方式大量处理选项?这是否影响速度?为最终用户带来的开销是否值得?
相关文章
相关标签/搜索