8.4 提供灵活的方法参数

在第7章中,我们看到了一些通过调整参数来使插件满足自己需求的例子。那些巧妙构思的插件通过定义恰当的默认值,并允许我们覆盖这些默认值提供了极大的灵活性。在轮到我们编写插件的时候,也应该替用户考虑到这一点。

为说明让插件用户定制插件行为的不同方式,我们来看一个例子,其中包含可以调整和修改的多项设置。这个例子是一个为元素块加投影的插件方法。同样的效果可以通过高级的CSS技术完成,但我们这里要使用一个更“暴力”的JavaScript方式:创建一些部分透明的元素,然后把它们相继排列在页面的不同位置上,参见代码清单8-11。

代码清单8-11

  1. (function($) {
  2. $.fn.shadow = function() {
  3. return this.each(function() {
  4. var $originalElement = $(this);
  5. for (var i = 0; i < 5; i++) {
  6. $originalElement
  7. .clone()
  8. .css({
  9. position: 'absolute',
  10. left: $originalElement.offset().left + i,
  11. top: $originalElement.offset().top + i,
  12. margin: 0,
  13. zIndex: -1,
  14. opacity: 0.1
  15. })
  16. .appendTo('body');
  17. }
  18. });
  19. };
  20. })(jQuery);

对于每个调用此方法的元素,都要复制该元素一定数量的副本,调整每个副本的不透明度。然后,再通过绝对定位方式,以该元素为基准按照不同的偏移量定位这些副本。现在,这个插件方法不接受任何参数,因此调用该方法很简单。

  1. $(document).ready(function() {
  2. $('h1').shadow();
  3. });

调用结果就是在标题下方添加了阴影效果,如图8-5所示。

8.4.1 参数对象 - 图1

图 8-5

接下来,我们就赋予这个插件方法一些灵活性。这个方法的操作取决于一些用户可能想要修改值。可以把这些值提取出来作为参数,以便用户根据需要修改。

8.4.1 参数对象

在介绍jQuery API时,我们曾看到过很多将对象作为(.animate()$.ajax()等)方法参数的例子。作为一种向插件用户公开选项的方式,对象要比刚刚使用的参数列表更加友好。对象会为每个参数提供一个有意义的标签,同时也会让参数次序变得无关紧要。而且,只要有可能通过插件来模仿jQuery API,就应该使用对象来提高一致性和易用性,参见代码清单8-12。

代码清单8-12

  1. (function($) {
  2. $.fn.shadow = function(options) {
  3. return this.each(function() {
  4. var $originalElement = $(this);
  5. for (var i = 0; i < options.copies; i++) {
  6. $originalElement
  7. .clone()
  8. .css({
  9. position: 'absolute',
  10. left: $originalElement.offset().left + i,
  11. top: $originalElement.offset().top + i,
  12. margin: 0,
  13. zIndex: -1,
  14. opacity: options.opacity
  15. })
  16. .appendTo('body');
  17. }
  18. });
  19. };
  20. })(jQuery);

这样,副本的数量和不透明度就可以自定义了。在这个插件中,可以通过函数参数options的属性来访问每一个值。

再调用这个方法则需要传递一个包含选项值的对象,而不是独立的参数了:

  1. $(document).ready(function() {
  2. $('h1').shadow({
  3. copies: 3,
  4. opacity: 0.25
  5. });
  6. });

配置能力得到了改进,但每次都必须提供两个选项才行。下一节,我们就来解决这个问题,看看怎么让用户可以忽略任何一个选项。

8.4.2 默认参数值

随着方法的参数逐渐增多,始终指定每个参数并不是必须的。此时,一组合理的默认值可以增强插件接口的易用性。所幸的是,以对象作为参数可以帮我们很好地达成这一目标,它可以为用户未指定的参数自动传入默认值,参见代码清单8-13。

代码清单8-13

  1. (function($) {
  2. $.fn.shadow = function(opts) {
  3. var defaults = {
  4. copies: 5,
  5. opacity: 0.1
  6. };
  7. var options = $.extend(defaults, opts);
  8. // ...
  9. };
  10. })(jQuery);

在这个方法的定义中,我们定义了一个新对象,名为defaults。实用函数$.extend()可以用接受的opts对象参数覆盖defaults中的选项,并保持选项对象中未指定的默认项不变。

接下来,我们仍然以对象调用同一个方法,但这次只指定一个有别于默认值的不同参数:

  1. $(document).ready(function() {
  2. $('h1').shadow({
  3. copies: 3
  4. });
  5. });

未指定的参数使用预先定义的默认值。$.extend()方法甚至可以接受null值,在用户可以接受所有默认参数时,我们的方法可以直接执行而不会出现JavaScript错误。

  1. $(document).ready(function() {
  2. $('h1').shadow();
  3. });

8.4.3 回调函数

当然,方法参数也可能不是一个简单的数字值,可能会更复杂。在各种jQuery API中经常可以看到另一种参数类型,即回调函数。回调函数可以极大地增加插件的灵活性,但却用不着在创建插件时多编写多少代码。

要在方法中使用回调函数,需要接受一个函数对象作为参数,然后在方法中适当的位置上调用该函数。例如,可以扩展前面定义的文本投影方法,让用户能够自定义投影相对于文本的位置,参见代码清单8-14。

代码清单8-14

  1. (function($) {
  2. $.fn.shadow = function(opts) {
  3. var defaults = {
  4. copies: 5,
  5. opacity: 0.1,
  6. copyOffset: function(index) {
  7. return {x: index, y: index};
  8. }
  9. };
  10. var options = $.extend(defaults, opts);
  11.  
  12. return this.each(function() {
  13. var $originalElement = $(this);
  14. for (var i = 0; i < options.copies; i++) {
  15. var offset = options.copyOffset(i);
  16. $originalElement
  17. .clone()
  18. .css({
  19. position: 'absolute',
  20. left: $originalElement.offset().left + offset.x,
  21. top: $originalElement.offset().top + offset.y,
  22. margin: 0,
  23. zIndex: -1,
  24. opacity: options.opacity
  25. })
  26. .appendTo('body');
  27. }
  28. });
  29. };
  30. })(jQuery);

投影的每个“切片”相对于原始文本都有不同的偏移量。此前,这个偏移量简单地等于切片的索引值。现在,偏移量都根据copyOffset()函数来计算,而这个函数是用户可以覆盖的参数。例如,用户可以在两个方向上指定负值偏移量:

  1. $(document).ready(function() {
  2. $('h1').shadow({
  3. copyOffset: function(index) {
  4. return {x: -index, y: -2 * index};
  5. }
  6. });
  7. });

这样会导致投影叠加起来并向左上方(不是向右下方)延伸,如图8-6所示。

8.4.1 参数对象 - 图2

图 8-6

回调函数可以像这样简单地修改投影方向,也可以根据插件用户的定义,对投影位置作出更复杂的调整。如果未指定回调函数,则会使用默认行为。

8.4.4 可定制的默认值

我们在前面已经看到了,通过为方法参数设定合理的默认值,能够显著改善用户使用插件的体验。但是,到底什么默认值合理有时候也很难说。如果用户脚本会多次调用我们的插件,每次调用都要传递一组不同于默认值的参数,那么通过定制默认值就可以减少很多需要编写的代码量。

要支持默认值的可定制,需要把它们从方法定义中移出,然后放到外部代码可以访问的地方,如代码清单8-15所示。

代码清单8-15

  1. (function($) {
  2. $.fn.shadow = function(opts) {
  3. var options = $.extend({}, $.fn.shadow.defaults, opts);
  4. // ...
  5. };
  6. $.fn.shadow.defaults = {
  7. copies: 5,
  8. opacity: 0.1,
  9. copyOffset: function(index) {
  10. return {x: index, y: index};
  11. }
  12. };
  13. })(jQuery);

默认值被放在了投影插件的命名空间里,可以通过$.fn.shadow.defaults直接引用。而对$.extend()的调用也必须修改,以适应这种变化。由于现在所有对.shadow()的调用都要重用defaults对象,因此不能让$.extend()修改它。我们就在此将一个空对象({})作为$.extend()的第一个参数,让这个新对象成为被修改的目标。

于是,使用我们插件的代码就可以修改默认值了,修改之后的值可以被所有后续对.shadow()的调用共享。而且,在调用方法时仍然可以传递选项。

  1. $(document).ready(function() {
  2. $.fn.shadow.defaults.copies = 10;
  3. $('h1').shadow({
  4. copyOffset: function(index) {
  5. return {x: -index, y: index};
  6. }
  7. });
  8. });

因为在此提供了新的默认值,以上脚本会创建带10个切片的投影。由于在调用方法时提供了copyOffset回调函数,所以投影也将朝向左下方,如图8-7所示。

8.4.1 参数对象 - 图3

图 8-7