jQuery插件 / 进阶

Provide Public Access to Default Plugin Settings

为了使用插件的程序员能更加直观的看到插件的参数(settings),我们常常会把这些数据暴露(expose)出来,比如下面例子中的$.fn.hilight.defaults
==> 将setting从插件中抽离出来

/* plugin definition */
$.fn.hiLight = function( options ) {
    var opts = $.extend({}, $.fn.hiLight.defaults, options);
};


/* plugin default - added as properties into the empty {} */
$.fn.hiLight.defaults = {
    foreground: "red",
    background: "yellow"
}
____________________________________________________________________________________

/* 一个叫Jim的程序员使用了该插件,并对它的设置进行了修改 */
/* This needs only be called once and does not hace to be called from within a "ready-block" */
$.fn.hiLight.defaults.foreground = "blue";


/* 按照自己的意愿修改完setting之后,Jim开始使用该插件了 */
$("#myDiv").hiLight();

=> And now we can call the plugin method like this and it will use a blue foreground color
Jim 也可以这样简化上面两句代码:

$("#myDiv").hiLight({
    foreground: "blue",
});

关于extend()函数:

$.extend( target [, object1 ] [, objectN ] )

- target:   Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
- object1:  可选, Object类型 第一个被合并的对象。
- object2:  可选, Object类型 第N个被合并的对象。
Provide Public Access to Secondary Functions as Applicable

To let others extend your custom plugin.
=> To expose the format function so that it can be redefined. In other words, you can say, we now are capable to write plugins for a plugin.
==> 将function从插件中抽离出来

// Plugin definition.
$.fn.hilight = function( options ) {
    // Iterate and reformat each matched element.
    return this.each(function() {
        var elem = $( this );
        // ...
        var markup = elem.html();       // html(): set content
        // Call our format function.
        markup = $.fn.hilight.format( markup );
        elem.html( markup );            // html(): return content
    });
};
 
// Define our format function.
$.fn.hilight.format = function( txt ) {
    return "<strong>" + txt + "</strong>";
};

关于html()函数:

$(selector).html()

The html() method sets or returns the content (innerHTML) of the selected elements.
- To return content, it returns the content of the FIRST matched element.
- To set content, it overwrites the content of ALL matched elements.
Keep Private Functions Private

虽然以上两种expose提高了plugin的可重写和灵活性。我们插件的大多数函数仍然需要是private的,举例:

(function($) {      // closure
    $.fn.hilight = function( options ) {
        debug(this);
    };

    function debug(obj) {
        if (window.console && window.console.log) {
            window.console.log("hilight selection count: " + obj.length);
        }
    }
})(jQuery);
Bob and Sue

先来看Bob小朋友写的插件:

jQuery.fn.superGallery = function( options ) {
    // Bob's default settings:
    var defaults = {
        textColor: "#000",
        backgroundColor: "#fff",
        fontSize: "1em",
        delay: "quite long",        // 控制动画延迟的参数
        getTextFromTitle: true,
        getTextFromRel: false,
        getTextFromAlt: false,
        animateWidth: true,
        animateOpacity: true,
        animateHeight: true,
        animationDuration: 500,
        clickImgToGoToNext: true,
        clickImgToGoToLast: false,
        nextButtonText: "next",
        previousButtonText: "previous",
        nextButtonTextColor: "red",
        previousButtonTextColor: "red"
    };
    var settings = $.extend( {}, defaults, options );
    return this.each(function() {
        // Plugin code would go here...
    });
};

另一位小朋友Sue决定用Bob的插件编写一些东西,当她想要修改 宽度动画变化速度 animateWidthDuration时,发现Bob写的插件中居然没有这个选项 >> 懵逼小脸(●' - '●)
==> It's not really about how many options your plugin has; but what options it has! 这是Bob犯的第一个错误,参数定义的不够全

Bob小朋友犯的第二个错误 来自参数 delay。他将delay的选项定义成了抽象的描述:

var delayDuration = 0;
switch ( settings.delay ) {
    case "very short":   delayDuration = 100;    break;
    case "quite short":  delayDuration = 200;    break;
    case "quite long":   delayDuration = 300;    break;
    case "very long":    delayDuration = 400;    break;
    default:             delayDuration = 200;
}

=> not only limit the level of control the people have(here I mean Sue or you), but also take quite a bit of place.
==> 应该就直接用数字

Give Full Control of Elements

If your plugin creates elements to be used within the DOM, then it's a good idea to give certain elements IDs or classes.
BUT note that your plugin sholdn't rely on these hooks INTERNALLY!
==> 用人话说,就是不要把选择元素的代码(比如$(".gallery-wrapper").append("...");)放在plugin的闭包里面!
反例:

$.fn.inLight = function( options ) {
    // ...
    $( "<div class='gallery-wrapper' />" ).appendTo( "body" );
    $( ".gallery-wrapper" ).append( "..." );
}

以上这种情况,有个隐患,万一用户使用该plugin的时候,想要修改元素的某些属性(比如颜色或者字体)将无法实现。
To allow users to access and even manipulate that information, you can store it in a variable containing the settings of your plugin.

$.fn.hilight = function( options ) {

    // ...

    var defaults = {
        wrapperAttrs : {
            class: "gallery-wrapper"
        },
    };
     
    // We can use the extend method to merge options/settings as usual:
    // But with the added first parameter of TRUE to signify a DEEP COPY:
    var settings = $.extend( true, {}, defaults, options );

    // Retain an internal reference
    var wrapper = $("<div />")
                    .attr( settings.wrapperAttrs )
                    .appendTo( settings.container );
};

// Easy to reference later
wrapper.append("...");

==> 用户可以通过定义的变量wrapper来修改类的名字, 同理,我们也可以修改元素的CSS样式,比如这里,我们将变量多设置一个css()方法

var defaults = {
    wrapperAttrs: {...};
    wrapperCSS: {...},
    // ... rest of settings
};

// Retain an internal reference:
var wrapper = $('<div />')
       .attr( settings.wrapperAttrs )
       .css( settings.wrapperCSS )        // 允许我们修改CSS样式
       .appendTo( settings.container );
Provide Callback Capabilities

What is a callback? – A callback is essentially a function to be called later, normally triggered by an event.

=> If your plugin is driven by events then it might be a good idea to provide a callback capability for each event.
==> Plus, you can create your own custom events and then provide callbacks for those.

var defaults = {
    /**
     *  We define an empty anonymous function
     *  so that we don't need to check its existence before calling it.
     *  because it exists
     */
    onImageShow: function() {},

    //... rest of settings...
}

nextButton.on("click", showNextImage);  // Event "Click" triggered the function showNextImage()

function showNextImage() {
    var image = getNextImage();     // Returns reference to the next image node

    //... code to show the image here

    settings.onImageShow.call(image);    // CALLBACK: image call the function onImageShow()
}

关于call()函数:

call( [ thisObj [,arg1 [, arg2 [, [,.argN ]]]]] ) 
call 方法可以用来代替另一个对象调用一个方法

---------------- 相似的方法 > apply() ----------------
apply([thisObj[,argArray]]) 
应用某一对象的一个方法,用另一个对象替换当前对象。 

---------------- 举例 ----------------
/**定义一个animal类*/  
    function Animal(){   
        this.name = "Animal";   
        this.showName = function(){   
            alert(this.name);   
        }   
    }   
/**定义一个Cat类*/  
    function Cat(){   
        this.name = "Cat";   
    }   
      
/**创建两个类对象*/  
    var animal = new Animal();   
    var cat = new Cat();   
      
/*通过call或apply方法,将原本属于Animal对象的showName()方法交给当前对象cat来使用了。*/   

    animal.showName.call(cat,",");           //输入结果为"Cat"   
    // 或 animal.showName.apply(cat,[]);   

---------------- 结论 ----------------
以上代码无论是采用animal.showName.call或是animal.showName.apply方法,运行的结果都是输出一个"Cat"的字符串。
说明showName方法的调用者被换成了cat对象,而不是最初定义它的animal了。这就是call和apply方法的妙用!

这里的callback初始化的方法比较特别
=> 我们调用了imagecontext
==> 也就是说,我们可以通过this访问当前的image对象, 比如:

$(" ul.img li ").superGallery({
    onImageShow: function() {
        $(this).after( "<span>" + $(this).attr("longdesc") + "</span>" )
    }
});

关于after()函数:

.after( content )
- Insert content, specified by the parameter, after each element in the set of matched elements. 
content can be one or more HTML string, DOM element, text node, array of elements and text nodes, or jQuery object to insert after each element in the set of matched elements.

.after(function)
- a function that returns aforementioned content
Remember, It's a Compromise

And equally, it's not going to be very useful if you offer no or very few methods of control. So, remember, it's always going to be a compromise.

Three things you must always take into account are:

  • Flexibility: How many situations will your plugin be able to deal with?
  • Size: Does the size of your plugin correspond to its level of functionality? I.e. Would you use a very basic tooltip plugin if it was 20k in size? – Probably not!
  • Performance: Does your plugin heavily process the options in any way? Does this affect speed? Is the overhead caused worth it for the end user?
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容