silvermyth

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  1 随笔 :: 12 文章 :: 1 评论 :: 0 Trackbacks
     先讲一下背景,最近发现项目中有些js文件的规模越来越大,接近2000行,开始出现维护困难的苗头;刚好实现的一个功能需要用到tree插件,在网上搜到一个bootstrap treeview插件可以用,但该插件无法支持懒加载和动态添加功能,网上现有的扩展方案都无法完全满足我的要求。花了一些时间看了bootstrap treeview的代码和Jquery插件的编写方法,对其进行了扩展并在项目中实现了一个简单的Select插件;另外计划把项目中以前组件化的自定义UI控件也全部用Jquery插件实现,以便复用并且清除冗余代码,这一过程我会陆续发布出来,本篇主要以我的select插件为例讲述如何才能编写一个Jquery插件:
    要想编写一个Jquery插件,首先必须对Jquery有所了解,尤其是以下几个知识点:
  •     知道(function($){...})(jQuery)是什么意思
    (function($){...})(jQuery)定义了一个函数,并且以jQuery为参数进行了调用,一般情况下,这是实现jQuery插件的标准姿势,对这个有兴趣的同学可以继续深入研究。
  •     $.proxy(fun, context)
    该方法返回一个fun函数的代理,返回的代理函数功能和fun相同,不同的是返回函数使用context参数作为调用上下文。这段话有点绕口,不明白的同学请参考:
    http://www.cnblogs.com/hongchenok/p/3919497.html
  •     $.each(arr, function(){...})
    该方法在每个arr中的元素上执行callback方法,存在很多变体。详细请参见http://www.jb51.net/article/24581.htm
  •     $.extend(deep, obj, obj1, ..)
    Jquery提供的用来扩展一个对象的方法,即将对象obj1合并到对象obj,一般被用来向jQuery对象中添加方法。详细请参考http://www.cnblogs.com/tianguook/p/4084061.html
  •     $.data(dom, data-name, data-value)
    Jquery提供的用来绑定对象到dom对象中的方法,如果没有定义data-value参数,是读取data-name的值;如果定义了data-value则是设置data-name的值。
      有了以上知识,我们来实现一个Select的Jquery插件,可以方便的创建、添加选项、删除选项等等。代码如下:
    /**
 * Created by gavinli on 17-3-30.
 
*/
;(function ($) {
    'use strict';

    var pluginName = 'myList';

    var _default = {};

    _default.settings = {};

    var MyList = function (element, options) {
        this.$element = $(element);
        this.init(options);
        return {
            init: $.proxy(this.init, this),
            add: $.proxy(this.add, this),
            remove: $.proxy(this.remove, this),
            list: $.proxy(this.list, this),
            clear: $.proxy(this.clear, this),
            getSelected: $.proxy(this.getSelected, this)
        }
    };

    MyList.prototype.init = function (options) {
        this.items = [];

        if (options.data) {
            if (typeof options.data === 'string') {
                options.data = $.parseJSON(options.data);
            }
            this.items = $.extend(true, [], options.data);
            delete options.data;
        }
        this.options = $.extend({}, _default.settings, options);
        this.render();
        this.subscribeEvents();
    };

    MyList.prototype.subscribeEvents = function () {
        //TODO:
    };

    MyList.prototype.add = function (items) {
        if (!(items instanceof Array)) {
            items = [items];
        }
        var _this = this;
        $.each(items, function (i, value) {
            _this.items.push(value);
        });
        this.filterDup();
        this.render();
    }

    //Remove all duplicated items
    MyList.prototype.filterDup = function () {
        var _this = this;
        var values = {}
        $.each(_this.items, function (i, value) {
            if (values[value]) {
                _this.items[i] = null;
            } else {
                values[value] = true;
            }
        });
    }

    MyList.prototype.remove = function (items) {
        var _this = this;
        var toBeRemoved = {};
        $.each(items, function (i, value) {
            toBeRemoved[value] = true;
        });
        $.each(_this.items, function (i, value) {
            if (toBeRemoved[value] == true) {
                _this.items[i] = null;
            }
        });
        this.render();
    }

    MyList.prototype.getSelected = function () {
        return this.$wrapper.val();
    }

    MyList.prototype.list = function (item) {
        var result = [];
        $.each(this.items, function (i, value) {
            if (value) {
                result.push(value);
            }
        });
        return result;
    }

    //Clear all items
    MyList.prototype.clear = function () {
        delete this.items;
        this.items = [];
        this.render();
    }

    MyList.prototype.render = function () {
        if (!this.initialized) {
            this.$wrapper = $(this.template.list);
            this.initialized = true;
        }
        //Append select element to $element
        this.$element.empty().append(this.$wrapper.empty());

        //Build select options
        this.buildList(this.items);
    }

    MyList.prototype.buildList = function (items) {
        var _this = this;
        $.each(items, function (i, value) {
            if (value) {
                var option = $(_this.template.item);
                option.append(value);
                _this.$wrapper.append(option);
            }
        });
    }

    MyList.prototype.template = {
        list: '<select multiple class="form-control"></select>',
        item: '<option></option>'
    };

    $.fn[pluginName] = function (options, args) {
        var result;
        this.each(function () {
            var _this = $.data(this, pluginName);
            if (typeof options === 'string') {
                if (!_this) {
                    //logError('Not initialized, can not call method : ' + options);
                }
                else if (!$.isFunction(_this[options]) || options.charAt(0) === '_') {
                    //logError('No such method : ' + options);
                }
                else {
                    if (!(args instanceof Array)) {
                        args = [args];
                    }
                    result = _this[options].apply(_this, args);
                }
            }
            else if (typeof options === 'boolean') {
                result = _this;
            }
            else {
                $.data(this, pluginName, new MyList(this, $.extend(true, {}, options)));
            }
        });
        return result || this;
    };

})(jQuery);
    下面针对其中的关键方法进行分析讲解:
    MyList函数:MyList对象的construnctor方法,接受options参数(options参数包含所有options的数组)
    MyList.prototype.init:根据options的data构建并渲染Select控件
    MyList.prototype.add:添加option到Select中并渲染,其它remove,list,getSelected方法大家自行研究
    上面代码中,最核心的部分在于如何将MyList对象和Dom元素结合、并且扩展到Jquery中,具体参考如下注释代码:
    ////扩展jQuery的prototype对象,这里的plugName等于myList,相当于给jQuery对象添加了一个"myList"方法
    $.fn[pluginName] = function (options, args) { 
        var result;
        ////这里的this是一个jQuery对象
        this.each(function () {    
            //下面的this不是jQuery对象,而是jQuery对象中的Dom对象
            //从Dom对象中获取"data-myList"属性绑定的对象
            var _this = $.data(this, pluginName);
            //options是方法名,例如$('#list1').MyList('add',[]),这里的options等于'add'
            if (typeof options === 'string') {
                if (!_this) {
                    //logError('Not initialized, can not call method : ' + options);
                }
                else if (!$.isFunction(_this[options]) || options.charAt(0) === '_') {
                    //logError('No such method : ' + options);
                }
                else {
                    if (!(args instanceof Array)) {
                        args = [args];
                    }
                    //调用MyList对象的方法
                    result = _this[options].apply(_this, args);
                }
            }
            else if (typeof options === 'boolean') {
                result = _this;
            }
            else {
                //创建MyList对象并绑定到Dom对象的data-myList属性
                $.data(this, pluginName, new MyList(this, $.extend(true, {}, options)));
            }
        });
        return result || this;
    };
   如何使用该插件的方法如下所示:
    首先在html中定义一个<div id="list1"></div>,然后这样使用它:
    //创建一个Select包含三个options
$('#list1').MyList(['Tom','Mary','Alice']);
//添加新的option
$('#list1').MyList('add', [['James','Richard']]);
//删除option
$('#list1').MyList('remove', [['Alice']]);
    最后我们可以在以上例子中发现创建jQuery插件的总体思路:
  •     自定义对象,对象中包含数据和jQuery对象本身
  •     定义对象的方法,并且根据对象中数据的变化渲染Dom对象(通过jQuery对象获得Dom对象)
  •     将该自定义对象方法扩展到jQuery原型对象中
  •     创建自定义对象,并绑定到jQuery中Dom对象的data属性
    通过以上实现,我们便可以像使用jQuery对象一样的方式使用控件,屏蔽对Dom元素的操作,简单又方便
posted on 2017-04-05 17:35 Gavin Li 阅读(121) 评论(0)  编辑  收藏 所属分类: JavaScript

只有注册用户登录后才能发表评论。


网站导航: