一個頁面使用兩次timepicker.js引起的Maximum call stack size exceeded問題解決辦法及bug修復

      可能大多數人用過,jquery ui組件中,有個叫datepicker的,用來接收用戶輸入的日期,截圖如下:

      

     這個控件的缺點是不能選擇時分秒,如項目中需要的輸入時分秒這樣的數據,就有點遺憾了。不過幸好有了下面這一款插件的出現,那就是timepicker,專門針對datepicker的這個弱點,添加了選擇時分秒的功能,截圖如下:

      

      這樣一來,就可以輸入帶有時分秒的日期時間了。


      本文正題來了:在同一個頁面中使用兩次或以上timepicker,會報一個錯誤:Maximum call stack size exceeded。同時瀏覽器卡死。查了下這個錯誤,是內存溢出,也就是說因爲不恰當的代碼,導致了遞歸或是死循環,所以瀏覽器卡死了。

      這可是個致命缺陷啊,比如我一個頁面中有兩處需要輸入日期時間,豈不是隻能看着瀏覽器卡死了?於是本人下決心找到這個問題。爲了從原理上瞭解到底是哪裏出現了遞歸或死循環,我決定把timepicker的源碼看一遍。(幸虧源碼還不算複雜,代碼量不多,而且變量命名規範)~

      沒看幾行就發現問題了,源碼從第30行開始,對datepicker的函數進行了重寫,典型代碼如下:

$.datepicker._connectDatepickerOverride = $.datepicker._connectDatepicker;
		$.datepicker._connectDatepicker = function(target, inst) {
			$.datepicker._connectDatepickerOverride(target, inst);
      可以看到,作者先將datepicker的connectDatepicker進行了保存,然後重新定義此函數,在重新定義的第一句,又把剛剛保存好的原函數執行了一遍。

      問題就出在這裏了,這個$.datepicker._connectDatepicker是個全局變量,第一次重寫的時候在裏面先執行了一次原函數,也就是說這個插件調用一次之後,$.datepicker._connectDatepicker就不是原來的$.datepicker._connectDatepicker了,已經被重寫了。那麼在調用第二次的時候,取到的$.datepicker._connectDatepicker已經發生了變化,然後又進行函數重寫,又把發生變化的$.datepicker._connectDatepicker執行了一遍,就出問題了。

      明白了原因,解決的辦法也就自然有了:如果已經重新定義過$.datepicker._connectDatepicker了,就不要讓他再重寫一遍了。加上判斷語句即可。修改後如下:

if(!$.datepicker._connectDatepickerOverride){
		$.datepicker._connectDatepickerOverride = $.datepicker._connectDatepicker;
		$.datepicker._connectDatepicker = function(target, inst) {
			$.datepicker._connectDatepickerOverride(target, inst);

      既判斷$.datepicker._connectDatepickerOverride是否已經存在。如果沒有,才進行重寫函數。

     嚴重提醒一下:在此函數下面還有一系列的重寫,都要進行相同的判斷!!!

      修改後運行一下代碼,世界美好了~

-----------------------------------------------------------------------

另外,我在使用timepicker的時候還發現了兩點bug,在此也提出來,供大家參考。

第一個:timepicker的彈出層的z-index問題。

jquery ui的datepicker彈出層的z-index是動態計算出來的,因爲每次彈出來的值都不一樣。而timepicker的值確是定死的,沒有跟隨datepicker一起計算。這樣,當頁面中還彈出其他一些層的時候,就會發生這樣的情況,如圖:

timepicker的層被擋住了,跟datepicker不在同一高度上。(在十分碰巧的時候才能遇到這種情況,很不幸,我就遇到了--!)

解決方法也很簡單,在timepicker的resize()函數中,添加關於z-index的代碼即可。修改後的代碼如下:

this.tpDiv.css({
            'height': dpDiv.height(),
            'top'   : dpDivPos.top,
            'left'  : dpDivPos.left + dpDiv.outerWidth() + 'px',
	    'z-index': dpDiv.css('z-index')
        });
讓它的z-index跟datepicker的一致就行。

第二個,一個頁面中使用多次的情況。

在timepicker的_generateHtml()函數中,有如下一句:

$('body').append(this.tpDiv);

也就是把拼接好的html代碼放到頁面上。作者此處有一點疏漏,在append之前未做任何檢測,如果頁面中已經有一個timepicker顯示了,再append一個,豈不就有兩個了。隨然之前的那一個是隱藏的,看不到,但是在取值的時候,你的jquery選擇器替你選到了兩個timepicker,(如果頁面中用了三次,那就是三個。。。),

截一個用了兩次的圖:


可以看到時分秒那裏重複出現了兩次,(應該是12:32:00)

所以此處也應該進行一下判斷,頁面中是否已經存在timepicker了。修改後的代碼如下:


if($('#'+this._mainDivId).length>0){
    $('#'+this._mainDivId).remove();
}
$('body').append(this.tpDiv);	

這一下,世界就又美好了~

--------------------------------------------------

最後補充一點,就是我使用的timepicker版本,是0.2.1,現在最新的好像有0.3了,不過去官網看了一下,好像變動比較大,使用方法不同了,而且秒也不能顯示了。官網:http://milesich.com/




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章