代码改变世界

自己写jquery插件之模版插件高级篇(一)

2015-12-21 09:43  杭伟  阅读(3905)  评论(4编辑  收藏  举报

需求场景

最近项目改版中,发现很多地方有这样一个操作(见下图gif动画演示),很多地方都有用到。这里不讨论它的用户体验怎么样。

仅仅是从复用的角度,如果每个页面都去写text和select元素,两个button按钮,增加add和delete对应的js函数,无疑大大增加了工作量和维护成本。

select有预设值的情况:

下面就开始动手把这4个html元素做成一个JQuery插件。

jquery插件结构

如果你是零基础,请参考Jquery官网对jquery plugin的介绍:http://learn.jquery.com/plugins/

整个插件做好后是一个js文件,我们首先来看下它的整体结构,如下图:

调用时的代码(无参):

$(".demo1").dlpcustomSelect();

调用时的代码(有参):

$(".demo1").dlpcustomSelect({
      addButtonText:'Add',
      delButtonText:'>>'
});

前台HTML:

<select class="demo1"></select>

这样就把插件定义的html元素全部渲染出来了,插件自带了相关js函数和功能。代码维护起来非常方便。

下面我们开始这个Jquery插件的制作过程:

一. 定义插件结构,插件名称,默认值和构造函数

1.插件结构,名称,默认值

新建dlpcustomselect.js文件后,我们首先书写这样的代码:

;(function ($, window, document, undefined) {
  //Author:HANGWEI
  //Create the defaults once
  var pluginName = 'dlpcustomSelect',
    defaults = {
      addButtonEnabled : true,
      addButtonText: 'Add',
      delButtonText:'Delete'
    };
//... other code ...

// A really lightweight plugin wrapper around the constructor,
  // preventing against multiple instantiations
  $.fn[ pluginName ] = function (options) {
    var args = arguments;

    // Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin.
    if (options === undefined || typeof options === 'object') {
      return this.each(function () {
        // If this is not a select
        if (!$(this).is('select')) {
          $(this).find('select').each(function(index, item) {
            // For each nested select, instantiate the dlp custom select
            $(item).dlpcustomSelect(options);//注意此处的插件名称
          });
        } else if (!$.data(this, 'plugin_' + pluginName)) {
          // Only allow the plugin to be instantiated once so we check that the element has no plugin instantiation yet

          // if it has no instance, create a new one, pass options to our plugin constructor,
          // and store the plugin instance in the elements jQuery data object.
          $.data(this, 'plugin_' + pluginName, new DlpCustomSelect(this, options));//注意此处插件的构造函数
        }
      });
      // If the first parameter is a string and it doesn't start with an underscore or "contains" the `init`-function,
      // treat this as a call to a public method.
    } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {

      // Cache the method call to make it possible to return a value
      var returns;

      this.each(function () {
        var instance = $.data(this, 'plugin_' + pluginName);
        // Tests that there's already a plugin-instance and checks that the requested public method exists
        if (instance instanceof DlpCustomSelect && typeof instance[options] === 'function') {//注意此处插件构造函数名
          // Call the method of our plugin instance, and pass it the supplied arguments.
          returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
        }
      });

      // If the earlier cached method gives a value back return the value,
      // otherwise return this to preserve chainability.
      return returns !== undefined ? returns : this;
    }

  };

})(jQuery, window, document);

 在上述代码中,pluginName是插件名称,defaults规定了插件的三个参数及其默认值;

 $.fn[pluginName]=function(options){};函数的功能,如代码注释所说,阻止多个插件实例被创建。

 ;(function ($, window, document, undefined) {  这句代码的详细解释请参考 这里

2. 构造函数

  // The actual plugin constructor
  function DlpCustomSelect(element, options) {
    this.element = $(element);
    // jQuery has an extend method which merges the contents of two or
    // more objects, storing the result in the first object. The first object
    // is generally empty as we don't want to alter the default options for
    // future instances of the plugin
    this.settings = $.extend({}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
  }

构造函数用于初始化和参数定义,如无特殊需求, 可参考上述写法来定义。

二. 编写插件核心部分代码

下面我们开始章节一代码结构中: other code 的部分

1. 构造函数名.prototype={...};

  1 DlpCustomSelect.prototype = {
  2     init: function () {
  3       // Add the custom HTML template
  4       this.container = $('' +
  5       '<div class="dlpcustomselect-container">' +
  6         '<table class="box1">' +
  7         ' <tr>' +
  8         '   <td><input class="waitAddValue" type="text" size="48" /></td>' +
  9         '   <td><input class="btn-pull-buttom" type="button" /></td>' +
 10         ' </tr>' +
 11         ' <tr>' +
 12         '   <td><select style="width: 265px;height: 100px" multiple="multiple"></select></td>' +
 13         '   <td><input class="btn-delete-buttom" type="button" /></td>' +
 14         ' </tr>' +
 15         '</table>'+
 16       '</div>')
 17         .insertBefore(this.element);
 18 
 19       // Cache the inner elements
 20       this.elements = {
 21         originalSelect: this.element,
 22         box1: $('.box1', this.container),
 23         filterInput1: $('.box1 .waitAddValue', this.container),
 24         select1: $('.box1 select', this.container),
 25         addButton: $('.box1 .btn-pull-buttom', this.container),
 26         deleteButton: $('.box1 .btn-delete-buttom', this.container)
 27       };
 28 
 29       // Set select IDs
 30       this.originalSelectName = this.element.attr('name') || '';
 31       var select1Id = 'dlpcustomselect-list_' + this.originalSelectName;
 32       this.elements.select1.attr('id', select1Id);
 33 
 34       // Apply all settings
 35       this.setAddButtonEnabled(this.settings.addButtonEnabled);
 36       this.setAddButtonText(this.settings.addButtonText);
 37       this.setDelButtonText(this.settings.delButtonText);
 38 
 39       //updateSelectionStates(this);
 40       // Hide the original select
 41       this.element.hide();
 42 
 43       bindEvents(this);
 44       refreshSelects(this);
 45 
 46       return this.element;
 47     },
 48     setAddButtonEnabled: function(value, refresh) {
 49       this.settings.addButtonEnabled = value;
 50       if (value) {
 51         this.container.find('.btn-pull-buttom').removeAttr("disabled");
 52       } else {
 53         this.container.find('.btn-pull-buttom').attr("disabled","disabled");
 54       }
 55       if (refresh) {
 56         //refreshSelects(this);
 57       }
 58       return this.element;
 59     },
 60     setAddButtonText: function(value, refresh) {
 61       this.settings.addButtonText = value;
 62       if (value) {
 63         this.elements.addButton.show().val(value);
 64         //if upper code type doesn't work,use this code.
 65         //this.container.find('.btn-pull-buttom').show().val(value);
 66       } else {
 67         this.elements.addButton.hide().val(value);
 68         //if upper code type doesn't work,use this code.
 69         //this.container.find('.btn-pull-buttom').hide().val(value);
 70       }
 71       if (refresh) {
 72         //refreshSelects(this);
 73       }
 74       return this.element;
 75     },
 76     setDelButtonText: function(value, refresh) {
 77       this.settings.delButtonText = value;
 78       if (value) {
 79         this.elements.deleteButton.show().val(value);
 80       } else {
 81         this.elements.deleteButton.hide().val(value);
 82       }
 83       if (refresh) {
 84         //refreshSelects(this);
 85       }
 86       return this.element;
 87     },
 88     getCustomData: function(){
 89       var terms = new Array();
 90       this.container.find('.box1 select option').each(function(index, item) {
 91         terms.push(item['value']);
 92       });
 93       return terms;
 94     },
 95     getContainer: function() {
 96       return this.container;
 97     },
 98     destroy: function() {
 99       this.container.remove();
100       this.element.show();
101       $.data(this, 'plugin_' + pluginName, null);
102       return this.element;
103     }
104   };
View Code

init:function(){...}  需要实现的init函数,这里我们用来创建html模板、应用所有的设置、调用绑定事件。

方法 refreshSelects(this)用来将前台select中的元素(如果有的话)copy到插件中。

方法 getCustomData用来返回插件的值

这里的设计思路是:隐藏你在前台写的select元素,返回插件模板中的自定义html串。另外,如果前台select中有option元素,则同步copy到插件中。

三. 绑定插件按钮事件和书写自定义功能函数

//bind events for button
  function bindEvents(dlpCustomSelect) {
    dlpCustomSelect.elements.addButton.on('click', function() {
      addOption(dlpCustomSelect);
    });
    dlpCustomSelect.elements.deleteButton.on('click', function() {
      deleteOption(dlpCustomSelect);
    });
      //backup method.
      /*
      $(document).on('click', '.box1 .btn-pull-buttom', function() {
          addOption(dlpCustomSelect);
      });
      */
  }

如以上代码所示,绑定插件的两个按钮的事件。其中addOption和deleteOption函数具体实现这里不再详细阐述。 

根据项目实际需要,增加自定义绑定事件和函数。

四. 完成并测试

调用插件:

$(".demo1").dlpcustomSelect({
                addButtonText:'Add',
                delButtonText:'>>'
            });

不更改参数值,直接调用(使用默认参数值):

$(".demo2").dlpcustomSelect();

获取插件中的值:$(".demo1").dlpcustomSelect('getCustomData')

demo下载

总结

本插件的实现大量参考了国外jquery插件制作的通用做法。希望本篇文章能起到抛砖引玉的作用,能引导开发人员自己动手写jquery插件。

本来想挂在github上,但鉴于这个插件的功能很少,就不费周章了。需要的直接在本篇文章下载吧。

另外本人水平有限,如有错误之处还请各位批评指正。

希望本文对你有帮助。