2010-07-01 66 views
3

簡短版本:我擴展了一個jQuery對象函數,該函數打算取0-3個參數並將它們作爲第二個到第四個參數傳遞到jQuery.animate。除此之外,我想隔離回調,並在其末尾添加另一個函數調用。擴展庫時追加jQuery回調函數


我正在擴展jQuery庫的普通方式,通過jQuery.fn.newfunction = ...。這是一個自定義動畫函數,具體來說就是展開一個對象,使其包含邊距的外部寬度等於其父節點的內部寬度。

jQuery.fn.expand_to_parent_container = function() { 
    var args = Array.prototype.slice.call(arguments); 

    return this.each(function(){ 
    var parent = $(this).parent(); 
    parent.inner_width = parent.innerWidth(); 

    var inner_outer_differential = $(this).outerWidth(true) - $(this).width(); 

    // store original width for reversion later 
    $(this).data('original_width', $(this).width()); 

    args.unshift({ width: parent.inner_width - inner_outer_differential }); 

    $.fn.animate.apply($(this), args); 
    }); 
} 

夠簡單。我正在使用arguments局部變量,將其「鑄造」爲一個數組,並且不移動第一個參數,該參數將出現在properties插槽.animateAPI here)中。因此,我們的意圖是允許.expand_to_parent_container採用參數duration, easing, callback,並採用通常的jQuery約定,使其在直觀模式下可選(例如,如果僅傳遞一個函數,則它將成爲回調函數)。似乎jQuery.speed負責解決這個混亂,但我寧願不直接使用它/篡改它,因爲它看起來不像其他jQuery函數那樣是public/deprecation-proof,並且因爲我會而只是將任務委託給.animate

這是我的回覆功能。

jQuery.fn.contract_to_original_size = function() { 
    var args = Array.prototype.slice.call(arguments); 

    var callback_with_width_reset = function(){ 
    callback(); 
    $(this).width('auto'); 
    } 

    args.unshift({ width: $(this).data('original_width') }); 

    if($(this).data('original_width')) { 
    $.fn.animate.apply($(this), args); 
    } else { 
    $(this).width('auto'); 
    } 
} 

我做出var callback_with_width_reset ...分配我明確期望的行爲,因爲我想爲width屬性被重置爲自動,這樣既可以更接近恢復到原來的狀態,並從任何瘋狂恢復可能發生在很多動畫過程中。但是,我不知道如何從argumentsargs中得出回調函數。

+2

+1。如果你決定打破它,你將不會成爲第一個,事實上jQuery UI 1.8已經打破了它。 – 2010-07-01 16:09:09

回答

1

有幾件事情,我想真正的快速指出:

  • 如果您一次對多個對象調用expand_to_parent_container,你保持unshifting {width: ...}對象爲args永不移回關閉。
  • contract_to_original_size不使用this.each()也不使用return this,它會遭受同樣的問題,一次又一次地不移動寬度對象。
  • 您可以確保你的代碼只設置original-width如果尚未設置,停止你的問題之一在這裏(在開始擴張,而擴張/不管不會亂用吧!)
  • 你可以在你的論點使用$.map()數組來檢測和替換你的回調函數很容易,如果你仍然覺得你需要。

這裏是調整一點點的代碼:識別與速度搞亂可能是壞的道路

jQuery.fn.expand_to_parent_container = function() { 
    // instead of Array.prototype, we can grab slice from an empty array [] 
    var args = [].slice.call(arguments); 

    return this.each(function(){ 
    var parent = $(this).parent(); 
    parent.inner_width = parent.innerWidth(); 

    var inner_outer_differential = $(this).outerWidth(true) - $(this).width(); 
    // store original width for reversion later 
    if (!$(this).data('original_width')) 
    { 
     $(this).data('original_width', $(this).width());  
    } 

    args.unshift({ width: parent.inner_width - inner_outer_differential }); 
    $.fn.animate.apply($(this), args); 
    args.shift(); // push the width object back off in case we have multiples 
    }); 
} 

jQuery.fn.contract_to_original_size = function() { 
    var args = Array.prototype.slice.call(arguments); 
    var callback; 

    var callback_with_width_reset = function(){ 
    if (callback) callback.apply(this,arguments); 
    // do something else like: 
    // $(this).width('auto'); 
    // you may want to $(this).data('original-width', null); here as well? 
    } 

    args = $.map(args, function(param) { 
    // if the parameter is a function, replace it with our callback 
    if ($.isFunction(param)) { 
     // and save the original callback to be called. 
     callback = param; 
     return callback_with_width_reset; 
    } 
    // don't change the other params 
    return param; 
    }); 

    // we didn't find a callback already in the arguments 
    if (!callback) { 
    args.push(callback_with_width_reset); 
    } 


    return this.each(function() { 
    // rather than shift() the new value off after each loop 
    // this is another technique you could use: 

    // create another copy so we can mess with it and keep our template 
    var localArgs = [].slice.call(args); 
    // also - storing $(this) is going to save a few function calls here: 
    var $this = $(this); 

    if($this.data('original_width')) { 
     localArgs.unshift({ width: $this.data('original_width') }); 
     $.fn.animate.apply($this, localArgs); 
    } else { 
     $this.width('auto'); 
    }  
    }); 
} 

Check it out in action on jsFiddle

+0

可愛。感謝您抽出寶貴的時間。這非常有效,我從優秀的代碼中學習了一些非常有用的實踐來討論可變範圍。我最終刪除了shift-unshift方法,因爲它比localArgs方法更不直觀。我還添加了一個檢查器,以便在當前寬度已經等於目標寬度時不調用'.animate'。 – 2010-07-01 18:13:47

0

我目前的解決方案,我想我會用,除非更好的東西來了走了,是這樣的:

var arguments_length = arguments.length; 
for(var i = 0; i < arguments_length; i++) { 
    if(typeof arguments[i] == 'function') { 
    var temp = arguments[i]; 
    arguments[i] = function(){ 
     temp(); 
     $(this).width('auto'); 
    } 
    } 
} 

在相關呵呵時刻Array.prototype.slice.call(arguments);返回值似乎不工作如預期。最初,我不是通過迭代arguments,而是將該原型調用設置爲分配給args,然後通過for(arg in args)進行迭代。但是有很多意想不到的行爲。我想現在不要這麼想。

這似乎工作,但它有兩個倒臺似乎並不重要,但仍然感覺錯誤。

  1. 它是如何檢測哪一個參數是回調函數是相對「愚蠢的」。
  2. 我對arguments[i]的重置非常依賴於不帶參數的函數。幸運的是,.animate中的回調函數確實沒有任何參數,所以現在並不是什麼大問題,但我懷疑一個更靈活的擴展會更聰明地處理這個問題。
+1

你的問題的第2部分很容易解決:'temp.apply(this,arguments)'而不是'temp()' – gnarf 2010-07-01 16:36:41