发布于:插件

如何创建基础插件

有时你希望在整个代码中使某项功能可用。例如,也许你想要一个可以在 jQuery 选择结果上调用的单一方法,用于对该选择执行一系列操作。在这种情况下,你可能需要编写一个插件。

链接 jQuery 工作原理 101:jQuery 对象方法

在编写我们自己的插件之前,我们必须首先了解一点 jQuery 的工作原理。看看这段代码:

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

这是一些非常基础的 jQuery 代码,但你知道幕后发生了什么吗?每当你使用 $ 函数选择元素时,它都会返回一个 jQuery 对象。该对象包含了你一直在使用的所有方法(.css().click() 等)以及所有符合选择器的元素。jQuery 对象从 $.fn 对象中获取这些方法。此对象包含所有 jQuery 对象方法,如果我们想编写自己的方法,也需要将其包含在内。

链接 基础插件编写

假设我们要创建一个插件,使一组检索到的元素内的文本变为绿色。我们只需在 $.fn 中添加一个名为 greenify 的函数,它就会像任何其他 jQuery 对象方法一样可用。

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

请注意,要使用 .css()(另一个方法),我们使用的是 this,而不是 $( this )。这是因为我们的 greenify 函数与 .css() 属于同一个对象。

链接 链式调用

这虽然可行,但为了让我们的插件在实际环境中生存,还需要做几件事。jQuery 的特性之一是链式调用,即你可以将五六个动作链接到一个选择器上。这是通过让所有 jQuery 对象方法再次返回原始 jQuery 对象来实现的(有少数例外:不带参数调用的 .width() 返回所选元素的宽度,且不可链式调用)。让我们的插件方法支持链式调用只需要一行代码:

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

链接 保护 $ 别名和添加作用域

$ 变量在 JavaScript 库中非常流行,如果你在 jQuery 中使用另一个库,则必须使用 jQuery.noConflict() 使 jQuery 不使用 $。然而,这将破坏我们的插件,因为它是在假设 $jQuery 函数别名的前提下编写的。为了与其他插件良好运行,并且 仍能使用 jQuery 的 $ 别名,我们需要将所有代码放在一个立即调用函数表达式 (IIFE)中,然后将 jQuery 传入该函数,并将参数命名为 $

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

此外,立即调用函数的主要目的是让我们拥有自己的私有变量。假设我们想要一种不同的绿色,并想将其存储在一个变量中:

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

链接 最小化插件占用空间

编写插件时,好的做法是在 $.fn 中只占用一个位置。这既降低了你的插件被覆盖的可能性,也降低了你的插件覆盖其他插件的可能性。换句话说,这样做是不好的:

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

更好的做法是只占用一个位置,并使用参数来控制该位置执行的操作。

1
2
3
4
5
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 对象通常被称为集合的原因。如果你想对特定元素进行任何操作(例如获取 data 属性、计算特定位置),那么你需要使用 .each() 来循环遍历这些元素。

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

注意,我们返回的是 .each() 的结果而不是返回 this。由于 .each() 本身已经是可链式调用的,它会返回 this,然后由我们返回。这比我们目前一直在做的方法能更好地维持链式调用。

链接 接收选项

随着你的插件变得越来越复杂,通过接收选项来使插件可定制是一个好主意。最简单的方法(尤其是选项很多时)是使用对象字面量。让我们修改 greenify 插件以接收一些选项。

1
2
3
4
5
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
5
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();

这个方便的插件会遍历集合中的所有锚点(anchor),并在括号中追加 href 属性。

1
2
3
4
5
<!-- 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
5
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 通过同名的 href 属性为我们提供了便捷的访问方式。