# 八、插件

## 在构建插件时使用`$`别名

当编写 jQuery 插件时，应该实现与常规的旧 jQuery 代码相同的冲突预防例程。考虑到这一点，所有插件都应该包含在私有范围内，在这个范围内`$`别名可以被使用，而不用担心冲突或令人惊讶的结果。

下面的编码结构应该看起来很熟悉，因为它在本书的几乎每个代码示例中都有使用，并在[第 1 章](02.html#_Chapter_1_Core)中进行了解释。

样本:sample82.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script> 
alert(jQuery(document).jquery);
        // Don't use $ here.
It is not reliable.  
        (function ($) {
            //
Can use $ without fear of conflicts.      
            alert($(document).jquery);
        })(jQuery);  </script>
</body>
</html>

```

## 新插件附加到`jQuery.fn`对象成为 jQuery 方法

新插件被附加到`jQuery.fn`对象，因为这是`jQuery.prototype`的快捷方式或别名。在下面的代码示例中，我们将计数插件添加到`jQuery.fn`对象中。通过这样做，我们创建了自己的自定义 jQuery 方法，可以在一组包装好的 DOM 元素上使用。

基本上，附加在`jQuery.fn`上的插件允许我们创建自己的自定义方法，类似于在应用编程接口中找到的任何方法。这是因为当我们将插件函数附加到`jQuery.fn`时，我们的函数包含在原型链中—`$.fn.count = function(){}`—用于使用 jQuery 函数创建的 jQuery 对象。如果这让你大吃一惊，请记住，在`jQuery.fn`中添加一个函数意味着插件函数中的关键字`this`将引用 jQuery 对象本身。

样本:sample83.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
$.fn.count = function () {
      var $this = $(this); //
"this" is the jQuery object.     
      $this.text('0'); //
Sets the counter start number to zero.     
      var myInterval =
window.setInterval(function () {
          // Interval for
counting.         
          var currentCount =
parseFloat($this.text()); var newCount = currentCount + 1; $this.text(newCount + '');
      }, 1000);
  };
  })(jQuery); jQuery('#counter1').count();  </script>
</body>
</html>

```

注意事项:

通过向 **`jQuery.fn`** 对象添加一个插件，我们实际上是说我们的插件想要使用 jQuery 函数来选择一个上下文(DOM 元素)。如果你的插件不需要特定的上下文(换句话说，一组 DOM 元素)来操作，你可能不需要把这个插件附加到 **`$.fn`** 上。将其作为实用函数添加到 jQuery 名称空间中可能更有意义。

## 在一个插件里面，`this`是对当前 jQuery 对象的引用

当您将插件附加到`jQuery.fn`对象时，附加插件函数内部使用的关键字`this`将引用当前的 jQuery 对象。

样本:sample84.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
$.fn.count = function () {
      // "this" is
equal to jQuery('#counter1').     
      alert(this); //
Alerts jQuery object.     
      alert(this[0]); //
Alerts div element.      
      alert(this[0].id); //
Alerts "counter1". 
  };
  })(jQuery); jQuery('#counter1').count();  </script>
</body>
</html>

```

关键是你要弄清楚关键词`this`在插件函数中到底指的是什么。

## 使用 `each()`迭代 jQuery 对象，并使用`this`关键字提供对对象中每个元素的引用

使用`each()`，我们可以为我们的插件创建一个隐式迭代。这意味着如果包装器集合包含多个元素，我们的插件方法将应用于包装器集合中的每个元素。

为此，我们使用 jQuery 实用程序`each()`函数，这是一个通用的对象和数组迭代器，基本上简化了循环。在下面的代码示例中，我们使用函数迭代 jQuery 对象本身。在传递给`each()`的函数内部，关键字`this`将引用 jQuery 包装器集中的元素。

样本:sample85.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <div id="counter2"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
       $.fn.count = function () {
           this.each(function () {
               //
"this" is the current jQuery object.             
               var $this = $(this);
               $this.text('0'); //
Sets the counter start number.             
               var myInterval =
window.setInterval(function () {
                   //
Interval for counting.                 
                   var currentCount =
parseFloat($this.text()); var newCount = currentCount + 1; $this.text(newCount + '');
               }, 1000);
           });
       };

 })(jQuery); jQuery('#counter1,
#counter2').count();  </script>
</body>
</html>

```

如果你想让一个插件使用隐式迭代，使用`each()`函数是至关重要的。

## 插件返回 jQuery 对象，因此 jQuery 方法或其他插件在使用插件后可以被链接

通常，大多数插件会返回 jQuery 对象本身，这样插件就不会中断链接。换句话说，如果一个插件不特别需要返回值，它应该继续这个链，这样附加的方法就可以应用到包装器集。在下面的代码中，我们用`return this;`语句返回 jQuery 对象，这样链接就不会中断。请注意，在我调用`count()`插件后，我正在链接`parent()`和`append()`方法。

样本:sample86.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <div id="counter2"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
$.fn.count = function () {
       return this.each(function () {
           //
Return the jQuery object, or "this" after each()             
           var $this = $(this);
           $this.text('0');
           var myInterval =
window.setInterval(function () { var currentCount =
parseFloat($this.text()); var newCount = currentCount + 1; $this.text(newCount + ''); }, 1000);
       });
   };
   })(jQuery); jQuery('#counter1,
#counter2').count().parent() // Chaining continues because jQuery object is
returned.     
       .append('<p>Chaining
still works!</p>');   </script>
</body>
</html>

```

注意事项:

通过简单地不返回 jQuery 对象，可以使插件成为一个破坏性的方法。

## 默认插件选项

插件通常包含默认选项，作为插件逻辑的基线默认配置。这些选项在插件被调用时使用。在下面的代码中，我正在创建一个包含单个属性(`startCount`)和值(`0`)的`defaultOptions`对象。该对象存储在计数功能`$.fn.count.defaultOptions`上。我们这样做是为了在插件之外配置选项。

样本:sample87.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <div id="counter2"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
$.fn.count = function () {
       return this.each(function () {
           var $this = $(this);             //
Sets the counter start number to zero.             

$this.text($.fn.count.defaultOptions.startCount + '');
           var myInterval =
window.setInterval(function () { var currentCount =
parseFloat($this.text()); var newCount = currentCount + 1; $this.text(newCount + ''); }, 1000);
       });
   }; $.fn.count.defaultOptions = {
startCount: 100 };
   })(jQuery); jQuery('#counter1,
#counter2').count();    </script>
</body>
</html>

```

## 自定义插件选项

通常，默认插件选项可以被自定义选项覆盖。在下面的代码中，我传入一个`customOptions`对象作为插件函数的参数。该对象与`defaultOptions`对象组合，创建一个单独的`options`对象。我们使用 jQuery 实用程序方法`extend()`将多个对象组合成一个对象。`extend()`方法为用新属性覆盖对象提供了完美的工具。有了这些代码，插件现在可以在调用时进行定制。在这个例子中，我们向`count`插件传递一个自定义数字(500)作为计数的起点。此自定义选项会覆盖默认选项(0)。

样本:sample88.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <div id="counter2"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
       $.fn.count = function (customOptions)
{
           //
Create new option, extend object with defaultOptoins and customOptions.        
           var options =
$.extend({}, $.fn.count.defaultOptions, customOptions);
           return this.each(function () {
               var $this = $(this); //
Sets the counter start number to the default option value             
               // or
to a custom option value if it is passed to the plugin.             

$this.text(options.startCount + '');
               var myInterval =
window.setInterval(function () { var currentCount =
parseFloat($this.text()); var newCount = currentCount + 1; $this.text(newCount + ''); }, 1000);
           });
       }; $.fn.count.defaultOptions = {
startCount: 100 };
   })(jQuery);  // Passing
a custom option overrides default. 
        jQuery('#counter1,
#counter2').count({ startCount: 500 });

    </script>
</body>
</html>

```

## 覆盖默认选项而不改变原始插件代码

因为默认选项可以从插件外部访问，所以可以在调用插件之前重置默认选项。当你想在不改变插件代码本身的情况下定义自己的选项时，这可能会很方便。这样做可以简化插件调用，因为从某种意义上来说，您可以根据自己的喜好全局设置插件，而无需分叉原始插件代码本身。

样本:sample89.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div id="counter1"></div>
    <div id="counter2"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
       $.fn.count = function (customOptions)
{
           var options =
$.extend({}, $.fn.count.defaultOptions, customOptions);
           return this.each(function () {
               var $this = $(this);
$this.text(options.startCount + '');
               var myInterval =
window.setInterval(function () { var currentCount =
parseFloat($this.text()); var newCount = currentCount + 1; $this.text(newCount + ''); }, 1000);
           });
       }; $.fn.count.defaultOptions = {
startCount: 100 };
   })(jQuery);  //
Overwrite default options. 

jQuery.fn.count.defaultOptions.startCount = 200; jQuery('#counter1').count(); //
Will use startCount: 200, instead of startCount:0 
        jQuery('#counter2').count({
startCount: 500 }); // Will overwrite any default values.     </script>
</body>
</html>

```

## 动态创建元素，以编程方式调用插件

根据插件的性质，插件的正常调用(通过 DOM 元素和事件)以及编程调用都是至关重要的。考虑一个对话插件。有时会根据用户事件打开模式/对话框。其他时候，需要根据环境或系统事件打开一个对话框。在这些情况下，您仍然可以通过动态创建一个元素来调用插件，而无需 DOM 中的任何元素。在下面的代码中，我通过首先创建一个元素来调用我的插件，从而在页面加载时调用`dialog()`插件。

样本:sample90.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <a href="#" title="Hi">dialog, say hi</a> <a href="#" title="Bye">dialog, say
    bye</a>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
       $.fn.dialog = function (options) { var text = this.attr('title') || this.text();
alert(text); };
   })(jQuery);
        jQuery('a').click(function () { //
Invoked by user event      
            $(this).dialog(); return false;
        });
        $(window).load(function () {     //
Create DOM element to invoke the plugin     
            jQuery("<a></a>").attr('title', 'I
say hi when invoked!').dialog(); // Run immediately. 
        });  </script>
</body>
</html>

```

显然，根据插件的选项、复杂性和功能，这种模式可能会有很多变化。这里的要点是，插件可以通过现有的 DOM 元素调用，也可以通过那些动态创建的元素调用。

## 提供回调和传递上下文

创作 jQuery 插件时，最好提供 [回调](http://stackoverflow.com/questions/824234/what-is-a-callback-function)函数作为选项，并在回调被调用时将这些函数传递给`this`的上下文。这为包装件组中的元素提供了附加处理的载体。在下面的代码中，我们将一个自定义选项传递给`outAndInFade()`插件方法，该方法是一个函数，应该在动画完成后调用。回调函数在被调用时被传递`this`的值。这允许我们在我们定义的函数中使用`this`值。当回调函数被调用时，关键字`this`将引用包装器集中包含的一个 DOM 元素。

样本:sample91.html

```js
<!DOCTYPE html>
<html lang="en">
<body>
    <div>Out And In Fade</div>
    <div>Out And In Fade</div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>   (function ($) {
$.fn.outAndInFade = function (customOptions)
{
       var options =
$.extend({}, $.fn.outAndInFade.defaultOptions, customOptions || {});
       return this.each(function () {
           $(this).fadeOut().fadeIn('normal', function () { //
Callback for fadeIn()                 
               //
Call complete() function, pass it "this".                
               if
($.isFunction(options.complete)) {

options.complete.apply(this);
               }
           });
       });
   };
       $.fn.outAndInFade.defaultOptions
= {
           complete: null // No
default function.     
       };
   })(jQuery); jQuery('div').outAndInFade({
       // Change
background-color of the element being animated on complete.     
       // Note:
"this" will refer to the DOM element in the wrapper set.     
       complete: function () { $(this).css('background', '#ff9'); }

  });  </script>
</body>
</html>

```