8.3 添加 jQuery 对象方法

jQuery中大多数内置的功能都是通过其对象实例的方法提供的,而且这些方法也是插件之所以诱人的关键。当函数需要操作DOM元素时,就是将函数创建为jQuery实例方法的好机会。

前面我们已经看到,添加全局函数需要以新方法来扩展jQuery对象。添加实例方法也与此类似,但扩展的却是jQuery.fn对象:

  1. jQuery.fn.myMethod = function() {
  2. alert('Nothing happens.');
  3. };

 jQuery.fn对象是jQuery.prototype的别名,使用别名是出于简洁的考虑。

然后,就可以在使用任何选择符表达式之后调用这个新方法了:

  1. $('div').myMethod();

当调用这个方法时会弹出一个警告框(文档中的每个<div>显示一次)。由于这里并没有在任何地方用到匹配的DOM节点,所以为此编写一个全局函数也是一样的。由此可见,一个合理的实例方法应该包含对它的上下文的操作。

8.3.1 对象方法的上下文

在任何插件方法内部,关键字this引用的都是当前的jQuery对象。因而,可以在this上面调用任何内置的jQuery方法,或者提取它包含的DOM节点并操作该节点。为了确定可以怎样利用对象的上下文,下面我们来编写一个小插件,用以操作匹配元素的类。

这个新方法接受两个类名,每次调用更换应用于每个元素的类。尽管jQuery UI有一个健壮的.switchClass()方法,甚至该方法都支持以动画方式切换类,但为了演示需要,我们还是自己再来写一个吧,参见代码清单8-8。

代码清单8-8

  1. //未完成的代码
  2. (function($) {
  3. $.fn.swapClass = function(class1, class2) {
  4. if (this.hasClass(class1)) {
  5. this.removeClass(class1).addClass(class2);
  6. }
  7. else if (this.hasClass(class2)) {
  8. this.removeClass(class2).addClass(class1);
  9. }
  10. };
  11. })(jQuery);
  12. $(document).ready(function() {
  13. $('table').click(function() {
  14. $('tr').swapClass('one', 'two');
  15. });
  16. });

首先,测试每个匹配的元素是否已经应用了class1,如果是,则将该类替换成class2。然后,再测试class2并在必要时替换成class1。如果两个类都不存在,则什么也不做。

在使用这个插件的代码中,我们为表格绑定了click处理程序,当单击表格时在每一个行上都调用.swapClass()。我们的目的是想把表头行的类one切换成two,把合计行的类two切换成one。然而,预期的结果并没有发生,如图8-3所示。

8.3 添加 jQuery 对象方法 - 图1

图 8-3

结果是每一行都应用了two类。为了纠正这个问题,需要基于多次选择的元素来正确地处理jQuery对象。

8.3.2 隐式迭代

读者大概还记得,jQuery的选择符表达式可能会匹配零、一或多个元素。因此,在设计插件时必须考虑到所有这些可能的情况。然而,我们在此调用的.hasClass()只会检查匹配的第一个元素。换句话说,我们应该独立检查和操作每一个元素。

要在无论匹配多个元素的情况下都保证行为正确,最简单的方式就是始终在方法的上下文上调用.each()方法;这样就会执行隐式迭代,而执行隐式迭代对于维护插件与内置方法的一致性是至关重要的。

在调用的.each()方法内部,this依次引用每个DOM元素,因此可以调整代码依次检测每个匹配的元素,并为它们应用相应的类,参见代码清单8-9。

代码清单8-9

  1. (function($) {
  2. $.fn.swapClass = function(class1, class2) {
  3. this.each(function() {
  4. var $element = $(this);
  5. if ($element.hasClass(class1)) {
  6. $element.removeClass(class1).addClass(class2);
  7. }
  8. else if ($element.hasClass(class2)) {
  9. $element.removeClass(class2).addClass(class1);
  10. }
  11. });
  12. };
  13. })(jQuery);

 this的含义意!

在对象方法体内,关键字this引用的是一个jQuery对象,但在每次调用的.each()方法中,this引用的则是一个DOM元素。

这样,再单击表格,切换类的操作就不会影响到不带有任何类的行了,如图8-4所示。

8.3 添加 jQuery 对象方法 - 图2

图 8-4

8.3.3 方法连缀

除了隐式迭代之外,jQuery用户也应该能够正常使用连缀行为。因而,我们必须在所有插件方法中返回一个jQuery对象,除非相应的方法明显用于取得不同的信息。返回的jQuery对象通常就是this所引用的对象。如果我们使用.each()迭代遍历this,那么可以只返回迭代的结果,参见代码清单8-10。

代码清单8-10

  1. (function($) {
  2. $.fn.swapClass = function(class1, class2) {
  3. return this.each(function() {
  4. var $element = $(this);
  5. if ($element.hasClass(class1)) {
  6. $element.removeClass(class1).addClass(class2);
  7. }
  8. else if ($element.hasClass(class2)) {
  9. $element.removeClass(class2).addClass(class1);
  10. }
  11. });
  12. };
  13. })(jQuery);

前面在调用了.swapClass()之后,如果想对元素再执行其他操作,必须通过一条新语句重新取得元素。而在添加return之后,就可以在我们的插件方法上面连缀内置的方法了。