JQuery 插件的编写

jquery的插件机制

为了方便用户创建插件,jquery提供了.extend()和.fn.extend()方法。
.extend(object) ,用于扩展jQuery类本身,也就是用来在jQuery类/命名空间上增加新函数,或者叫静态方法. 例如 jquery内置的 ajax方法都是用.ajax()这样调用的,有点像 “类名.方法名” 静态方法的调用方式。

$.extend({
    minValue: function (a, b) {
        // 比较两个值,返回最小值
        return a < b ? a : b;
    }
});
//调用
var i = 100; j = 101;
var min_value = $.minValue(i, j); // min_value 等于 100
$.extend([deep], target, object1, [objectN]);
用一个或多个其他对象来扩展一个对象,返回被扩展的对象。
参数
    deep:可选。如果设为true,则递归合并。
    target:待修改对象,如果不指定target,则给jQuery命名空间本身进行扩展。
    obj1:待合并到第一个对象的对象
    objectN:可选。待合并到第一个对象的对象。
例:
    var obj1 = { a: false, b: 1, c: "abc" }; 
    var obj2 = { a: true, c: "def" }; 
    $.extend(obj1, obj2);
    // 输出 obj1 = { a: true, b: 1, c: "def" }
套路:
    var opts = $.extend({}, defaluts, options);
    // 用自定义插件参数去覆盖插件的默认参数

$.fn.extend(object)扩展 jQuery 元素集来提供新的方法(通常用来制作插件)。

$.fn = $.prototype = {
   init: function( selector, context ) {.....};
};

原来 .fn =.prototype,也就是jQuery对象的原型。那jQuery.fn.extend()方法就是扩展jQuery对象的原型方法。

自执行的匿名函数/闭包

匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。

封装JQuery插件

接下来我们一起来写个 监听文本框,动态显示下拉选项 (类似搜索提示)插件

1.定一个闭包区域,防止插件"污染"

//闭包限定命名空间
(function ($) {
    // do something
})(window.jQuery);

2.jQuery.fn.extend(object)扩展jquery 方法,制作插件

//闭包限定命名空间
(function ($) {
    $.fn.extend({
        getAjaxOption:function(options){
            // do something
        }
    });
})(window.jQuery);

3.给插件默认参数,实现 插件的功能
//闭包限定命名空间

(function ($) {
    $.fn.extend({
        getAjaxOption: function (options) {
            var opts = $.extend({}, defaluts, options); // 使用jQuery.extend 覆盖插件默认参数
            // 为body绑定事件,判断当点击别的区块,隐藏下拉选项区块
            $(document).on('click',function(event){
                event = window.event || event; 
                var target = $(event.srcElement || event.target);  
                var is_select = opts.container_class+' *,'+opts.container_class+','+opts.target_class+' *,'+opts.target_class;
                if(!target.is(is_select)){
                    $(opts.container_class).hide();
                }
            })
            return this.each(function () {  // 这里的this 就是 jQuery对象,这里 return 为了支持链式调用
                // 遍历所有选中的dom
                var $this = $(this); // 获取当前dom 的 jQuery对象,这里的this是当前循环的dom
                console.log(opts);  // opts为当前所有的配置项
                // 接下来,在当前dom下生成下拉选项的dom元素
                $this.parent().css('position','relative');
                $this.addClass(opts.target_class.slice(1))
                     .after('<div class="'+opts.container_class.slice(1)+'">'+
                        '<div class="'+opts.container_class.slice(1)+'-box">'+
                            '<div class="'+opts.no_data_class.slice(1)+'">'+opts.no_data_msg+'</div>'+
                        '</div>'+
                     '</div>');
            });
        }
    });
    //默认参数
    var defaluts = {
        container_class:'.container-options',   // 下拉选项容器class
        target_class:'.ajaxOptionThis',         // 当前dom的标识class 
        option_class:'.item',                   // 生成的下拉选项的class
        no_data_class:'.no-data',               // 无数据时对应项的class
        no_data_msg:'未找到相关数据',              // 无数据时对应项显示的文字信息
         ajax_url:'******',                     // 请求的地址
        ajax_type:'get',                        // 请求的类型
        ajax_data:{                             // 请求的额外参数
            city : 'zg',
            page : 1
        },
        ajax_keyword_name:'kw',                 // 请求的关键词的name值,value值固定为当前文本框的值
    };
})(window.jQuery);

5.插件私有方法
有些时候,我们的插件需要一些私有方法,不能被外界访问。

//私有方法,检测用户传进来的参数是否合法
function isValid(options) {
    return !options || (options && typeof options === "object") ? true : false;
}

6.公共方法 给别人来扩展你的插件 或者 返回一些回调api

// 公共方法
// 当点击了选项后的回调函数,$this为当前输入框,that为当前点击的选项,opts为插件的配置项集合
$.fn.getAjaxOption.clickCallback = function ($this,that,opts) {}
// 当ajax返回成功后执行的回调函数,$this为当前输入框,response为后端返回的数据,opts为插件的配置项集合
$.fn.getAjaxOption.successCallback = function ($this,response,opts) {}

完整的插件代码如下:

<style>
.container-options{position:absolute;min-width:100%;left:0;background:#fff;z-index:1;display:none}
.container-options .container-options-box{margin-top:-1px;border:1px solid #ccc;max-height:150px;overflow:auto}
.container-options .item,.container-options .no-data{text-align:center;transition:all .3s;cursor:pointer;line-height:1;padding:8px 0;text-align:center}
.container-options .item:hover{color:#fff;background:#eb6120}
.container-options .no-data{display:none}
</style>
(function ($) {
    $.fn.extend({
        getAjaxOption: function (options) {
            //检测用户传进来的参数是否合法
            if (!isValid(options)) return this;
            var opts = $.extend({}, defaluts, options);
            $(document).on('click',function(event){
                event = window.event || event; 
                var target = $(event.srcElement || event.target);  
                var is_select = opts.container_class+' *,'+opts.container_class+','+opts.target_class+' *,'+opts.target_class;
                if(!target.is(is_select)){
                    $(opts.container_class).hide();
                }
            })
            return this.each(function () { 
                var $this = $(this);
                $this.parent().css('position','relative');
                $this.addClass(opts.target_class.slice(1))
                     .after('<div class="'+opts.container_class.slice(1)+'">'+
                                '<div class="'+opts.container_class.slice(1)+'-box">'+
                                    '<div class="'+opts.no_data_class.slice(1)+'">'+opts.no_data_msg+'</div>'+
                                '</div>'+
                            '</div>');
                // 为当前输入框绑定 事件监听,动态获取值
                $this.on('input',function(){
                    var data = {};
                    data[opts.ajax_keyword_name] = $.trim($this.val());
                    data = $.extend({}, opts.ajax_data, data);
                    if( data[opts.ajax_keyword_name].length <=0 ){ return false; }
                    $.ajax({
                        type:opts.ajax_type,
                        url :opts.ajax_url,
                        data: data,
                        success: function(response){
                            $.fn.getAjaxOption.successCallback($this,response,opts);
                        }
                    });
                })
                // 为下拉选项的每一项绑定click事件
                $(opts.container_class).on('click',opts.option_class,function(){
                    $.fn.getAjaxOption.clickCallback($this,this,opts);
                })
            });

        }
    });
    //默认参数
    var defaluts = {
        // 容器dom
        container_class:'.container-options',
        target_class:'.ajaxOptionThis',
        option_class:'.item',
        no_data_class:'.no-data',
        no_data_msg:'未找到相关数据',
        ajax_url:'*****',
        ajax_type:'get',
        ajax_data:{
            city : 'zg',
            page : 1
        },
        ajax_keyword_name:'kw',
    };
    //公共方法
    $.fn.getAjaxOption.clickCallback = function ($this,that,opts) {
        $this.val($(that).text());
        $(opts.container_class).hide();
    }

    $.fn.getAjaxOption.successCallback = function ($this,response,opts) {
        $(opts.container_class).show().find(opts.option_class).remove();
        if(response.data == "no result"){
            $('.no-data',opts.container_class).show();
        }else{
            $('.no-data',opts.container_class).hide();
            var list = '';
            for (var i=0; i<response.data.list.length; i++){
                list += '<div class="'+opts.option_class.slice(1)+'" data-id="'+response.data.list[i].id+'">'+response.data.list[i].name+'</div>';
            }
            $(opts.container_class+'-box').append(list);
        }
    }
    //私有方法
    function isValid(options) {
        return !options || (options && typeof options === "object") ? true : false;
    }
})(window.jQuery);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容