如何寫一個jQuery插件

jQuery 插件開發模式

jQuery 的插件開發模式主要有三種:javascript

  • 經過$.extend()來擴展jQuery
  • 經過$.fn向jQuery添加新的方法
  • 經過$.widget()應用jQuery UI 的部件工廠方式建立

這裏咱們選用第二種:css

$.fn.myplugin = functin() {
    // plugin code
}

由於這種方法是加在jQuery對象上,能夠在jQuery選擇器選擇元素後直接調用:html

$('body').myplugin();

若對其餘兩種方法有興趣,請自行查看jQuery 官方文檔java

一個很是簡單的例子

改變元素的背景顏色插件:jquery

$.fn.tinyPlugin = function(){
    this.css('background-color','#fff'); // 這裏的this是jQuery對象,而不是原生的js對象
}

這樣寫的話,顏色不能使用時定義,修改一下:git

$.fn.tinyPlugin = function(bg){
    this.css('background-color',bg);
}

以上是一個jQuery插件的基本結構。但編寫更復雜的jQuery插件,藉助面向對象的思想可使插件具備更好的擴展性、可維護性。github

面向對象開發插件

大致思路以下:

1.建立myplugin構造函數segmentfault

2.定義myplugin的方法api

3.添加到jQuery對象中緩存

基本結構

首先將代碼放到一個自執行的匿名函數中,防止全局對象污染:
根據上面的步驟,基本結構大體以下:

// 定義構造函數
function myplugin($element,options){
    // ...
}

// 添加方法
myplugin.prototype = {
    method1: function(){
        // ...
    },
    method2: function(){
        // ...
    }
};

// 添加到jQuery對象中
$.fn.myplugin = function(options){
    new myplugin(this,options);
}

後面將在這個結構上進行完善:

放到當即執行的匿名函數內部

放到匿名函數內部的主要做用是隔離做用域,避免變量污染,而自執行能夠返回須要的函數或對象,而不須要每次經過條件判斷爲處理,只須要在首次加載的時候賦值給某個變量。更詳細的信息參見知乎上的 這個問題

;(function($){
    // ...
})(jQuery);

留意到上面,函數前還加了分號,這是因爲自執行函數前省略分號的表達式時,某些狀況下會報錯,能夠看 這裏

在匿名函數裏面還傳入了jQuery對象,這樣作的好處是美圓符號 $ 在內部是一個私有變量,僅表明jQuery,能夠防止和其餘使用 $ 的庫衝突。

有的自執行函數還將window對象、undefined對象傳入,具體做用能夠看 這裏

默認參數

options是提供給使用者定義的參數。一個好的插件,應該提供必要的參數給使用者,而且提供好默認的參數,由於不少狀況下不是每一個參數都須要自定義。以前在項目中寫了一個加載更多 LoadMore 插件,就以這個插件爲例:

function LoadMore($element,options){
    this.$element = $element;

    this._defaults = {
        loadingSelector: ".zc_loading",  //加載中的提示
        overSelector: ".zc_loaded",  // 加載完畢的加載
    }
    this.options = $.extend({},this._defaults,options||{});  // 合併參數
}

初始化方法

通常插件都會有一個 init() 方法,調用插件時作一些初始化操做:

function myplugin($element,options){
    this.init();
}

// 添加方法
myplugin.prototype = {
    init: function(){
        // ...
    }
};

防止屢次初始化

爲了防止用戶屢次在同一個元素上調用插件,有必要加一些已初始化的判斷。經過在該元素上用 data() 方法緩存插件的實例對象,每次實例化時判斷該緩存對象便可。

$.fn.myplugin = function(options){
    if (!$.data(this, 'myplugin')) {
        $.data(this, 'myplugin', new myplugin(this, options));
    }
}

遍歷全部元素

$ 選擇器返回的有多是多個元素,所以須要返回的全部元素添加插件,直接使用jQuery自帶的 each() 方法:

$.fn.myplugin = function(options){
    this.each(function(){
        if (!$.data(this, 'myplugin')) {
            $.data(this, 'myplugin', new myplugin(this, options));
        }
    });
}

注意這裏的this 是jQuery對象,而不是原生JS對象。因此能夠直接使用this.each()

保持鏈式操做

因爲jQuery有個鏈式操做的特性,在每一個方法調用後都會返回該對象,所以只須要 return 該對象便可保持鏈式操做:

$.fn.myplugin = function(options){
    return this.each(function(){
        // ...
    });
}

給插件添加方法

插件不只有可配置的參數,不少時候還須要調用方法。好比說「加載更多」插件,可能須要知道如今加載到第幾頁,或者是否已經所有加載完畢。此時就須要調用方法來得到結果。

在前面咱們知道是在 prototype 定義方法:

LoadMore.prototype = {
        init: function(){},
        // 從新加載
        reload: function(){},
        // 開始加載
        startLoad: function(){},
        // 本次加載完成
        loadOver: function(){},
        // 所有加載完畢
        allLoadOver: function(){},
        // 插入內容
        insert: function(content){}
    }

方法名能夠經過options參數傳入,只需在內部作字符串判斷:

$.fn.LoadMore = function(options,para){
        var instance;
        instance = $.data(this,'LoadMore');
        if(!instance){
            instance = new LoadMore(this,options);
            $.data(this,'LoadMore',instance);
        }

        // 若是是字符串則調用方法
        if($.type(options) === 'string') {
            return instance[options](para);
        }
        return this;  // 保持鏈式
}

所以咱們調用方法能夠像下面這樣:

$('#list').LoadMore('startLoad');

可是,當調用的方法須要傳參怎麼辦?很簡單,那就再加一個參數 para

$.fn.LoadMore = function(options,para){
        // ...
        // 若是是字符串則調用方法
        if($.type(options) === 'string') {
            return instance[options](para);
        }
        return this;  // 保持鏈式
}

調用帶參數的方法:

$('#list').LoadMore('insert','這是待插入的內容');

這裏若是要使用回調函數,和上面也是同樣的道理,只需在內部加個函數判斷。不詳述。如有更多的參數,請使用對象 {}

配置參數中添加事件

在寫 LoadMore 插件的過程當中,還發現須要使用事件,好比說滾動到某個位置時,執行某些操做。也很簡單,事件咱們能夠直接寫在配置參數 options 中:

this._defaults = {
    onScroll: null,  // scroll中
    onScrollBottom: null  // scroll到底部
}

而後在內部 init() 函數中調用前判斷:

init: function(){
    var that = this,
        $win = $(window),
        $doc = $(document);
    $win.on('scroll',function(){
        (typeof that.options['onScroll'] == 'function') && that.options['onScroll']();
        if($win.scrollTop() + $win.height() >= $doc.height()-10){
            (typeof that.options['onScrollBottom'] == 'function') && that.options['onScrollBottom']();
        }
    });
}

兼容AMD規範

如今流行模塊化開發,AMD是require.js在推廣過程當中的規範化產出,AMD說明見 這裏
下面的代碼可使你的插件兼容AMD規範,能夠在require.js中直接調用,固然,沒有使用require.js直接 <script> 引入也是能夠的。

// amd support
(function(factory){
    // amd support
    if(typeof define === 'function' && define.amd){
        define(['jquery'],factory);
    }else {
        factory(jQuery);
    }
}(function($){
    var exports = {};
    // 這裏寫插件代碼
    return exports;
}));

上面的前一段是判斷是否有define函數,有則調用define方法定義一個模塊,不然直接執行。後一段的 exports 對象是模塊的返回對象,模塊對外的接口。關於AMD規範的具體信息,可參考阮一峯的 這篇文章

結語

上面所寫的就是寫一個插件的大體流程,因爲能力有限,還有不少要完善的地方。想要了解更多,這裏推薦一篇文章 深刻理解jQuery插件開發。另外,這裏提供一個gitHub上面的某位大神寫的插件 SlipHover供參考。

相關文章
相關標籤/搜索