2013年4月15日 星期一

jQuery UI 的日期選擇器 datepicker 測試

近兩周都在玩 jQuery UI 的日曆小工具 (或日期選擇器 datepicker), 就是讓使用者可以直接在日曆上選取日期的小工具, 而且日期格式可以指定. 這個 widget 小工具根據書上講是 jQuery UI 最古老, 功能選項也最龐大的一個, 照官網範例三兩下就看到效果了, 很簡單啊, 原本以為兩天就可以搞定, 沒想到弄了快兩個禮拜. 最初看到這麼多選項, 我想又不是每一個都會用到, 只要測試一下最常用的就好了, 後來才發現, 如果要 localization (本地化, 就是繁體中文化啦), 還真的要用到許多看來用不到的選項, 據說 2.0 版時此 widget 會重新改寫.
通常在網頁上用來輸入日期的通常是文字欄位的 input 元素 (我常用的是三個下拉式選單組合) :

<input id="datepicker1" type="text" />

這個陽春的日期輸入介面最大的缺點是, 使用者可能會亂輸入, 資料庫要的是 2013-04-15, 但輸入的可能是 4/16/2013, 而且要一一鍵入不方便. 有了 jQuery UI 後, 就可以呼叫 input 元素的 jQuery 物件的 datepicker() 方法來將 input 元素改裝成美美的日曆選取器 :

$("#datepicker1").datepicker();  

這樣一來, 只要滑鼠在 input 文字欄位上點一下取得焦點後, 就會在 input 元素正下方出現一個日曆供選取日期, 例如 :

測試範例 1http://tony1966.xyz/test/jquerytest/datepicker_1.htm [看原始碼]

範例 1 中放置了三個日期選取器 :

$("#datepicker1").datepicker();
$("#datepicker2").datepicker({firstDay: 1});
$("#datepicker3").datepicker({appendText: "點一下顯示日曆"});

由 datepicker1 可知, 沒有傳入任何參數時, 日暦預設是英文版的, 而且預設一周是從星期天開始算的. 如果要更改一周之始, 必須傳入參數去設定日曆物件的 firstDay 選項. 如同其他 widget, 傳入參數最常用的是物件實體, 以 key:value 方式指定日曆的各個選項. 選項 firstDay 預設值為 0 (代表星期天), 星期一是 1, ... 星期六是 6. 在 datepicker2 我們將 firstDay 設為 1, 因此日曆的第一欄就是星期一. 另外, 選取日期後 input 元素預設顯示 mm/dd/yy 格式, 這不是我喜歡的 yy-mm-dd 格式, 這要用 dateFormat 選項設定, 稍後再介紹. 在 datepicker3 我們設定了選項 appendText 之值, 此字串會顯示在 input 元素後面, 通常用來作為提示之用.
先來處理本地化, 如果要顯示繁體中文, 就必須傳入特定的七個參數, 把預設的英文版覆蓋掉, 這叫做本地化 (localization). 本地化牽涉之選項有如下七個 :

星期 : dayNames (提示), dayNamesMin (星期欄位名稱)
月份 : monthNames (標題月份全名), monthNamesShort (標題月份下拉式選單簡名)
按鈕 : prevText (上月按鈕提示文字), nextText (次月按鈕提示文字)
週數 : weekHeader (週欄位名稱)

繁體中文化

var opt={
   dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],
   dayNamesMin:["日","一","二","三","四","五","六"],
   monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],
   monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],
   prevText:"上月",
   nextText:"次月",
   weekHeader:"週",
   showMonthAfterYear:true,
   dateFormat:"yy-mm-dd"
   };
$("#datepicker1").datepicker(opt);

其中, 前七個便是繁體中文化所必須的七個選項, 第八個選項 showMonthAfterYear 是設定月份顯示在年份後面; 最後一個選項 dateFormat 則是設定 input 元素內之日期格式為例如 2013-04-15. 關於 dateFormate 選項的格式字串整理如下 :

 d     每月的第幾日, 個位數時前面不補 0
 dd 每月的第幾日, 兩位數, 個位數時前面補 0
 o 每年的第幾日, 小於 100 時前面不補 0
 oo 每年的第幾日, 三位數, 小於 100 時前面補 0 為 3 位數
 D 星期的簡稱, 如 Fri
 DD 星期的全名, 如 Friday
 m 每年的第幾月, 個位數時前面不補 0
 mm 每年的第幾月, 兩位數, 個位數時前面補 0
 M 月份簡稱, 例如 Jan
 MM 月份全明, 例如 January
 y 兩位數的年, 個位數時前面補 0, 例如 05
 yy 四位數的年, 例如 2005
 @ 自 1970/1/1 至今之毫秒數
 ! 自 0001/1/1 至今之奈秒數 (tick)
 '' 跳脫上述之模式字串

最後一個格式為單引號, 用來跳脫其他的格式字串, 例如要在 input 中顯示 "Selected yy-mm-dd", 因 Selected 中含模式字串 d, 必須跳脫, 故要寫成 "Selecte'd' yy-mm-dd", 把 d 字元用單引號括起來即可. 其實我都一律使用 yy-mm-dd, 這最簡單明瞭. 其實這個 "yy-mm-dd" 字串在 datepicker 的管理物件 (manager object) 中也有定義三個常數是 "yy-mm-dd" 的 :
$.datepicker.ATOM
$.datepicker.ISO_8601
$.datepicker.W3C
也就是說, {dateFormat: "yy-mm-dd"} 可以寫成 {dateFormat: $.datepicker.W3C}, 效果是一樣的. 我覺得還是 "yy-mm-dd" 比較好記.

測試範例 2http://tony1966.xyz/test/jquerytest/datepicker_2.htm [看原始碼]

如果網頁中不只用到一個日期選取器, 則每一個都要如上設定那七個選項似乎有點煩, 如果要一勞永逸, 可以使用 jQuery UI datepicker 管理物件 (manager object) 的 setDefaults() 方法來設定預設值, 這樣此網頁中任何一個日期選取器都會套用此繁體中文化設定 :

繁體中文化 (使用 setDefaults)

$.datepicker.regional['zh-TW']={
   dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],
   dayNamesMin:["日","一","二","三","四","五","六"],
   monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],
   monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],
   prevText:"上月",
   nextText:"次月",
   weekHeader:"週"
   };
$.datepicker.setDefaults($.datepicker.regional["zh-TW"]);
$("#datepicker1").datepicker({dateFormat:"yy-mm-dd",showMonthAfterYear:true});

其實, jQuery UI 早已做好國際化了, 它已經為各種語言寫好本地化 js 設定檔, 這些設定檔放在 jQuery UI 下載檔解壓縮後的 \ui\i18n 目錄下. 只要在匯入 jQuery UI 函式庫後, 接著匯入所需要語言的設定檔即可, 例如繁體中文設定檔為 jquery.ui.datepicker-zh-TW.js, 把它放在專案的 jquery 目錄下, 然後於網頁的 head 中匯入即可 :

<script type="text/javascript" src="http://tony1966.xyz/test/jquery/jquery.ui.datepicker-zh-TW.js"></script>

這樣只要直接呼叫 datepicker() 方法就可以本地化了, 不需再自行設定 (當然客製化除外).
$("#datepicker1").datepicker();
世界主要語言的本地化檔案如下表, 我已將這些檔案放在 http://tony1966.xyz/test/jquery/ 下, 其餘見下載檔解壓縮的 \ui\i18n 目錄 :

 語言 本地化檔名
 繁體中文 jquery.ui.datepicker-zh-TW.js
 簡体中文 jquery.ui.datepicker-zh-CN.js
 德語 jquery.ui.datepicker-de.js 
 法語 jquery.ui.datepicker-fr.js
 西班牙語 jquery.ui.datepicker-es.js
 阿拉伯語 jquery.ui.datepicker-ar.js
 俄語 jquery.ui.datepicker-ru.js
 日語 jquery.ui.datepicker-ja.js
 韓語 jquery.ui.datepicker-ko.js

以下是這九種語言的本地化日曆, 可以發現 datepicker 中文簡體與繁體其實沒什麼差別.

測試範例 2-1 (繁體) : http://tony1966.xyz/test/jquerytest/datepicker_2_1.htm [看原始碼]
測試範例 2-2 (簡體) : http://tony1966.xyz/test/jquerytest/datepicker_2_2.htm [看原始碼]
測試範例 2-3 (日文) : http://tony1966.xyz/test/jquerytest/datepicker_2_3.htm [看原始碼]
測試範例 2-4 (韓文) : http://tony1966.xyz/test/jquerytest/datepicker_2_4.htm [看原始碼]
測試範例 2-5 (俄文) : http://tony1966.xyz/test/jquerytest/datepicker_2_5.htm [看原始碼]
測試範例 2-6 (阿文) : http://tony1966.xyz/test/jquerytest/datepicker_2_6.htm [看原始碼]
測試範例 2-7 (西文) : http://tony1966.xyz/test/jquerytest/datepicker_2_7.htm [看原始碼]
測試範例 2-8 (德文) : http://tony1966.xyz/test/jquerytest/datepicker_2_8.htm [看原始碼]
測試範例 2-9 (法文) : http://tony1966.xyz/test/jquerytest/datepicker_2_9.htm [看原始碼]
測試範例 2-10 (全部) : http://tony1966.xyz/test/jquerytest/datepicker_2_10.htm [看原始碼]

在測試範例 2-10, 我們把這九種語言整合在一起, 可以點擊國旗圖案 (來自 www.icondrawer.com), 利用 beforeShow 選項呼叫回呼函式來選擇本地化語系 :

var locale=$.datepicker.regional[""];
$("#en").click(function() {locale=$.datepicker.regional[""];});
$("#ar").click(function() {locale=$.datepicker.regional["ar"];});
....
$("#datepicker1").datepicker({beforeShow: set_locale});
function set_locale() {return locale;}
選項 beforeShow  用來設定日曆顯示前要呼叫的函式, 此回呼函式可以傳回選項物件實體, 以便在開啟日曆前先進行設定. 此處回呼函式傳回 $.datepicker.regional[] 來設定語系, 請注意, 本地化陣列 regional[] 之索引, 英文要設為空字串 [""].

接著我們來測試 showMonthAfterYear 與 showWeek 這兩個選項.

測試範例 3http://tony1966.xyz/test/jquerytest/datepicker_3.htm [看原始碼]

在範例 3 中, 放置了兩個日期選取器 :
$("#datepicker1").datepicker({dateFormat:"yy-mm-dd",showMonthAfterYear:true});
$("#datepicker2").datepicker({dateFormat:"yy-mm-dd",showWeek:true});
其中 datepicker1 將 showMonthAfterYear 設為 true, 所以日曆標題顯示的是 "2013年 四月"; 而 datepicker2 則為預設 false, 顯示的是 "四月 2013年", 但其 showWeek 選項設為 true, 所以日曆第一欄會顯示週數.
接下來要介紹三個選項 : changeYear, changeMonth 與 yearRange. 在前面範例中, 日曆的標題只是顯示年月字串, 如果要跳到去年或明年某月, 必須不斷按標題左右兩個按鈕 (次月, 上月) 有點麻煩 (循序式). 而 changeYear 與  changeMonth 此兩個選項若設為 true, 就會把標題中的年月字串分別改成下拉式選單, 可以直接選擇要顯示的哪一年哪一月日曆.

測試範例 4 : http://tony1966.xyz/test/jquerytest/datepicker_4.htm [看原始碼]

在範例 4 中, 放置了三個日期選取器 :
$("#datepicker1").datepicker({changeYear: true, changeMonth: true});
$("#datepicker2").datepicker({changeYear: true, changeMonth: true,
                                               yearRange:"2010:2015"});
$("#datepicker3").datepicker({changeYear: true, changeMonth: true,
                                               yearRange:"-5:+5"});
可見三個日曆標題中的年月都變成下拉式選單. 其中從 datepicker1 可知 yearRange 未設定的話是系統年份的前後十年 (不含系統之年份). 在 datepicker2 與 datepicker3 中, 我們測試了 yearRange 的兩種設值方法, 第一種是以 "from:to" 字串方式, 第二種是系統年份為準, 用 - 表示往前幾年, 用 + 表示往後幾年來指定下拉式選單的範圍.

接著我們來測試 showOtherMonths 與 selectOtherMonths 這兩個選項, 前者設為 true 表示要顯示日曆中銜接上月底與下月初的日期, 以便把整個日曆填滿, 預設 false 不顯示; 但這些銜接日期預設都無法選取, 要靠後者設為 true 才行.

測試範例 5 : http://tony1966.xyz/test/jquerytest/datepicker_5.htm [看原始碼]
在範例 5 中放置了兩個日期選取器 :
$("#datepicker1").datepicker({showOtherMonths: true});
$("#datepicker2").datepicker({showOtherMonths: true, selectOtherMonths: true});

可見銜接日期會以較淡顏色顯示, datepicker1 中的銜接日期雖顯示但無法選取, 而 datepicker2 的則可以選取.

範例六我們來測試一下 dafaultDate,  minDate 與 maxDate 這三個跟日期有關的選項, 其設定值格式都一樣. 選項 dafaultDate 用來設定日曆的預設日期, 也就是黃色高亮度的日期. 日曆打開時預設是讀取系統今日日期, 而我們設定 dafaultDate 選項就是要把這個系統日期覆蓋掉, 改為我們指定之日期. 而 minDate 與 maxDate 則是用來限制可選取日期範圍的選項. 選項 minDate 用來設定最小可選取日期, 小於此設定值之日期將不可選取; maxDate 用來設定最大可選取日期, 大於此設定值之日期將不可選取. 設定值可以是日期物件, 距離系統日期之日數, 或者是日期字串. 使用日期物件時要注意, Javascript 的日期物件函式 new Date(年,月, 日) 的參數中, 月是從 0 開始的, 因此四月要用 3 代入. 若 dafaultDate,  minDate 與 maxDate 使用日期字串, 則必須先用 dateFormat 選項設定其日期格式, 否則會解讀成錯誤的日曆.

測試範例 6 : http://tony1966.xyz/test/jquerytest/datepicker_6.htm [看原始碼]

在範例 6 中放置了三個日期選取器 :
$("#datepicker1").datepicker({dateFormat:"yy-mm-dd",
                                               dafaultDate: "2013-04-16", minDate: "2013-04-03"});
$("#datepicker2").datepicker({dafaultDate: new Date(2013, 3, 16),
                                               minDate: "-5", maxDate: "10"});
$("#datepicker3").datepicker({dafaultDate: new Date(2013, 3, 16),
                                               minDate: "-1y -1m -1w", maxDate: "+1y +1m +1w"});

第一個日曆中, 我們用 defaultDate 將預設日期設為 2013-04-16, 這樣不管哪一天顯示此網頁, 這日曆都會顯示 2013 年四月, 且 4 月 16 日高亮度. 其次, minDate 設為 "2013-04-03", 表示四月份中最小可選取日期為 4 月 3 日 (含), 4/1 與 4/2 都不可選取. 因為只設定 minDate, 沒有設定 maxDate, 因此 4/3 以後都是可選的, 而且次月按鈕都可以按, 而前月按鈕已經失能, 無法再按了. 特別注意這裡我們使用了日期字串來設定 minDate, 因此必須事先以 dateFormat 選項設定其格式, 要不然會剖析錯誤, 顯示 2018 年 10 月的日曆.
第二個日曆我們使用日期物件來設定 defaultDate, 用距離 (offset) 預設日期之日數來設定 minDate 與 maxDate. 因為 Javascript 的 Date 物件中, 月份為 0 起始的, 因此預設日期 2013 年 4 月 16 日要寫成 new Date(2013,3,16). 而 minDate: "-5" 表示最小可選取日為預設日期前 5 日, 也就是 16-5=11, 四月十一日; 而 maxDate: "10", 表示最大可選取日為預設日期後 10 日, 也就是 16+10=26, 四月二十六日. 注意, 因為我們限制可選日期為 4/3~4/26, 因此前月與次月按鈕都失能無法再按了, 如果將 changeYear 與 changeMonth 都設為 true, 則下拉式選單也會被限制選項只剩下 2013 年四月而已.
其實日期字串除了絕對值外, 也可以用相對值, 這是相對於預設日期而言. 在第三個日曆 datepicker3 中, minDate 與 maxDate 就使用了相對值. 其中 "-" 表示在預設日期之前, "+" 表示在預設日期之後, "y" 表示年, "m" 表示月, "d" 表示日, 所以 minDate: "-1y -1m -1w" 表示最小可選取日期為預設日期前一年一個月又一天, 預設日期是 2013-04-16 的話, 則 minDate 就是 2012-03-09; 同理 maxDate 則是 2014-05-23, 請按上月與次月按鈕印證.
關於限制可選取日期, 其實還有一個更常用的情況, 那就是將週六週日都設為不可選取. 請看測試範例 7 :

測試範例 7 : http://tony1966.xyz/test/jquerytest/datepicker_7.htm [看原始碼]

這裡用到 datepicker 管理物件的內建函式 $.datepicker.noWeekends(), 只要將日曆的 beforeShowDay 選項設定為呼叫此函式即可 :
$("#datepicker1").datepicker({beforeshowDay: $.datepicker.noWeekends});

選項 beforeShowDay 用來在顯示日期選取器中的每一天前, 呼叫指定的回呼函式, 如果回呼函式傳回的陣列中, 索引 0 為 true, 便將該日設為可選取, 反之 false 就設為不可選取. 它主要用來覆寫與星期有關的預設行為. 唯一傳入回呼函式的參數是星期, 例如 Friday. 而公用函式 $.datepicker.noWeekends 的作用就是判斷傳入的星期, 若是 Saturday 或 Sunday, 就把傳回陣列的索引 0 設為 false, 其他星期設為 true.

以上就是 datepicker 小工具常用的選項測試. 接下來要測試如何直接用程式設定 datepicker 的日期 (不是靠選取), 以及如何取得日曆小工具的日期. 這不是傳入選項物件, 而是在第一參數傳入 "動作字串" (actions) 與相關參數 params, 其格式為 :
$("#datepicker1").datepicker("action", params)

接著要介紹比較少用的四個選項, showOn, buttonImage, buttonImageOnly, buttonText. 通常我們都是擺個 input 元素當作日期選取器的承載元素, 當使用者點一下文字欄位, 就會自動打開日曆 , 這其實是 showOn 這個選項預設值為 "focus" 的緣故. 此選項有三個值 : "focus", "button", 以及 "both". 預設 "focus" 表示只要 input 元素一取得焦點, 日曆就會打開. 如果設為 "button", 則 input 元素後面會自動多出一個標題文字為 "..." 的按鈕, 這時點選 input 文字欄位就不會打開日曆了, 而是要按後面的那個按鈕, 如測試範例 8_1.
如果嫌 "..." 按鈕不知所云, 可以利用 buttonText 設定其標題文字, {buttonText: "日曆"}, 如測試範例 8_2. 也可以在按鈕上放置圖片來取代文字, {buttonImage: "calendar.gif"}, 當然這要上傳一個日曆圖檔到伺服器才行 (這個圖檔可以在 jQuery UI 下載檔解壓縮目錄 development-bundle\demos\datepicker\images 下找到). 這時即使同時有設定 buttonText, 按鈕上也只顯示圖片, 如測試範例 8_3.
因為按鈕使用 HTML 原生按鈕, 無法套用主題布景, 如果嫌按鈕難看, 也可以將 buttonImageOnly 選項設定為 true, {buttonImageOnly: true}, 這樣就會只顯示圖片, 不顯示按鈕, 如測試範例 8_4. 最後, 前述 showOn 選項設為 "button" 後, 點文字欄位不會再打開日曆, 而是必須按按鈕或圖片, 如果想魚與熊掌兼得的話, 那麼 showOn 選項要設為 "both", 如測試範例 8_5.

測試範例 8_1 : http://tony1966.xyz/test/jquerytest/datepicker_8_1.htm [看原始碼]
測試範例 8_2 : http://tony1966.xyz/test/jquerytest/datepicker_8_2.htm [看原始碼]
測試範例 8_3 : http://tony1966.xyz/test/jquerytest/datepicker_8_3.htm [看原始碼]
測試範例 8_4 : http://tony1966.xyz/test/jquerytest/datepicker_8_4.htm [看原始碼]
測試範例 8_5 : http://tony1966.xyz/test/jquerytest/datepicker_8_5.htm [看原始碼]

下面要介紹與選取事件有關的選項 onSelect, 以及 setDate, getDate, option 這三個動作.

測試範例 9 : http://tony1966.xyz/test/jquerytest/datepicker_9.htm [看原始碼]

此範例稍為複雜, 網頁元素有四個, 一個 input 做日曆輸入, 三個 button 按鈕, 第一個用來設定 input 的日期, 第二個用來取得選取的日期, 並用訊息盒顯示此日期, 第三個用來切換 showWeek 選項, 藉以說明 option 動作的 getter 與 setter 功能. 訊息盒需要一個 div 元素. 訊息盒用法參見 "jQuery UI widget 之確認盒應用範例" 這篇說明.

<input id="datepicker1" type="text" />
<button id="button1">設定日期為 2013-04-01</button>
<button id="button2">取得日期</button>
<div id="msgbox1" title="訊息"></div>

當使用者點一下 input 文字欄位, 在日曆中選取一個日期後, 就會顯示訊息盒告知被選取之日期. 這是加入 onSelect 選項的效果, 其值為一自訂之回呼函式 show_select(), 由這函式來打開訊息盒.

$("#datepicker1").datepicker({dateFormat:"yy-mm-dd",
                                               onSelect: show_select
                                               });
function show_select() {
   var date=$("#datepicker1").datepicker("getDate");
   $("#msgbox1").html("選取的日期 : " + date);
   $("#msgbox1").dialog("open");      
   }

當使用者按 button1 時, 呼叫 datepicker() 方法執行 setDate 動作, 把 input 之日曆設為選取 2013 年 4 月 1 日.

$('#button1').click(function(){
    $("#datepicker1").datepicker("setDate",new Date(2013,3,1));
    });

當使用者按下 button2 時, 呼叫 datepicker() 方法執行 getDate 動作, 它會傳回被選取日期之日期物件. 我們可以呼叫日曆的公用函式 formatDate 來把日期物件格式化為字串, 最後把字串用訊息盒顯示出來 :

$('#button2').click(function(){
    var date=$("#datepicker1").datepicker("getDate");
    date=$.datepicker.formatDate("yy 年 M d 日", date);
    $("#msgbox1").html("選取的日期 : " + date);
    $("#msgbox1").dialog("open");
    });

公用函式 formatDate 的語法為  : $.datepicker.formatDate(format, date[, options]), 第一參數為格式字串, 第二參數為日期物件, 有需要還可以傳入選項物件實體.
繼續來看看 dialog 動作, 此動作是要在對話盒中打開日曆, 而非在 input 元素的下方, 這通常要用一個按鈕來啟動, dialog 動作的參數如下 :

$("#datepicker1").datepicker("dialog", defaultDate, onselect[, options]);

其中第二參數用來設定日曆的預設日期, 可以是日期物件, 例如 new Date(2013,3,16), 或者是日期字串 "2013-04-16", 注意, 使用日期字串時必須帶入第四參數, 設定日期格式選項, 例如 {dateFormat: "yy-mm-dd"}. 如果傳入空字串 "" 表示預設為系統日期. 第三參數是選取日期後要呼叫的回呼函式, 通常用來設定 input 文字欄位元素之值.

範例 9 的第三個按鈕是用來測試 option 動作, 按下可以切換 showWeek 選項之值, 亦即 true 變 false, 或反之. option 動作可以傳入兩個參數 (getter) 或三個參數 (setter), 第二個參數要傳入選項名稱 (字串)  :

取得選項之值 : datepicker("option", optionName)
設定選項之值 : datepicker("option", optionName, value)

當按下第三個按鈕時, 先用 getter 取得目前 showWeek 之值, 再進行切換 :

var showWeek=$("#datepicker1").datepicker("option", "showWeek");
if (showWeek) {$("#datepicker1").datepicker("option", "showWeek", false);showWeek=false;}
else {$("#datepicker1").datepicker("option", "showWeek", true);showWeek=true;}

總之, 設定選項有兩種方法, 一是使用選項物件實體, 二是使用 option 動作; 但取得選項之值就只能用  option 動作.

測試範例 10 : http://tony1966.xyz/test/jquerytest/datepicker_10.htm [看原始碼]

此範例中有兩個元素, 一個是放置被選取日期的 input 元素, 一個是用來打開日曆的圖形 (也可以用按鈕或 a 元素) :

<input id="datepicker1" type="text">
<img id="open_calendar" src="calendar.gif">

當按下圖形時, 就呼叫 datepicker() 執行 dialog 動作, 選取日期後呼叫回呼函式去設定 input 元素之值 :

$("#datepicker1").datepicker("dialog", "2013-02-16", setDate, {dateFormat: "yy-mm-dd"});
function setDate(date) {$("#datepicker1").val(date);}

最後要介紹 datepicker 的公用函式, 有如下 5 個 :
  1. $.datepicker.setDefaults([options])
  2. $.datepicker.formatDate(format, date [,options])
  3. $.datepicker.parseDate(format, date_str [,options]) 
  4. $.datepicker.iso8601Week(date) 
  5. $.datepicker.noWeekends()
其中  setDefaults, formatDate, noWeekends 前面已經說明過了, 範例 11 主要測試 $.datepicker.parseDate() 與 $.datepicker.iso8601Week().
formatDate 與 parseDate 是作用相反的函式, formatDate 是將日期物件按指定格式轉成日期字串傳回; 而 parseDate 則是將指定格式之日期字串轉成日期物件傳回. 而 iso8601Week 則是計算日期物件在一年中之周數傳回.

測試範例 11 : http://tony1966.xyz/test/jquerytest/datepicker_11.htm [看原始碼]

此範例中, 特地將  showWeek 選項打開以資與 iso8601Week 計算之結果比較印證, 兩者是一樣的. 首先以 getDate 取得被選取之日期物件, 先用 formatDate 轉成日期字串, 再用 parseDate 又轉回日期物件.

var date=$("#datepicker1").datepicker("getDate");
var date_str=$.datepicker.formatDate("yy-mm-dd", date);
var date_parsed=$.datepicker.parseDate("yy-mm-dd", date_str);
var week=$.datepicker.iso8601Week(date);
var msg=date_str + " 是第 " + week + " 週<br>" +
              date_str + " 之日期物件內容 : " + date_parsed;
$("#msgbox1").html(msg);
$("#msgbox1").dialog("open");

以上就是 datepicker 的測試結果, 很長啊, 真是累死我了.

2013-11-27 補充 :

感謝網友戴兄的回應, 如果要對特定欄位, 例如指定星期一與星期四底色, 我查了 jQuery UI 文件, 其 API 中似乎僅 beforeShowDay 這個選項可以用, 但我測試的結果, 卻僅在各日期邊框套色, 並無法整個抽換掉底色 (那是用圖片做的). 原因是 beforeShowDay 乃是 jQuery UI 在描繪日立之前執行的設定, 即使指定了 background-image, 但描繪後全都被蓋掉.

如下範例 12 所示, 我將 themes\hot-sneaks\images 下的這張紅色的底圖取出改名為 hightlight.png :
ui-bg_diagonals-small_50_ff3853_40x40.png
然後用 beforeShowDay 這個選項來設定 :

    $(document).ready(function(){
      $("#datepicker1").datepicker({beforeShowDay: function(d) {
        var day=d.getDay();
        if (day==1 || day==4) {return [true,"myclass","Wow!"];}
        else {return [true,"","Oops!"];}
        }});
      });

這裡 beforeShowDay 必須指定為一個三個元素之陣列, [0] 為 true/false, 標示該日期是否可選, [1] 為一樣式名稱字串, 表示該日期要套用之樣式, [2] 則為設定該日期提示字串. 我們以傳入日期物件 d 的匿名函式來傳回此陣列, 取得該日期的星期後, 若為星期一或星期四, 就傳回套用指定樣式之陣列, 否則傳回沒有套用任何樣式之陣列.

樣式 myclass內容如下 :

.myclass {color:white;background-image:url("hightlight.png");}

測試範例 12http://tony1966.xyz/test/jquerytest/datepicker_12.htm [看原始碼]

如果不套用底圖, 改套用底色, 效果可能較明顯, 如範例 13 所示 :

 .myclass {color:white;background-color:red;}

測試範例 13http://tony1966.xyz/test/jquerytest/datepicker_13.htm [看原始碼]

這是目前找到的方法, 或許還有更好的方法也說不定.

至於戴兄的第二個問題, 希望設定今日的截止時間, 超過就不能選, 這主要是 Javascript 日期物件操控問題, datepicker 部分仍然跟上面問題一樣, 需要用 beforeShowDay 選項, 方法如範例 14 :

測試範例 14 : http://tony1966.xyz/test/jquerytest/datepicker_14.htm [看原始碼]

在範例 14 中, 我用 Date 物件的 getTime() 方法來取得自 1970/1/1 以來的毫秒數, 據此來控制傳回給 beforeShowDay 的陣列第一元素是 true/false 來控制可選與否. 我在日曆前面放了另外一個 text 輸入來設定截止時間, 預設為 12:00:00, 每次點日期選擇器時, jQuery UI 會去描繪日曆物件, 這時我們就可以在 beforeShowDate 選項裡, 利用回呼函式去判斷每一個傳入的日期, 搭配現在時間去判斷該日日期是否要設定為可選 :

  <input id="deadline" type="text" value="12:00:00">
  <input id="datepicker1" type="text">

以下回應戴兄另一詢問, 即是否可以讓輸入日期的 text 欄位僅容許 datepicker 挑選日期, 不讓使用者自行輸入, 這確實是一個實用的問題, 既然用了 jQuery UI, 當然不能還讓使用者可以亂填日期格式, 這問題只要在 input 元素中加入 readonly 來限制即可, 如下範例 15 所示 :

<input id="datepicker1" type="text" readonly="true" > 或者
<input id="datepicker1" type="text" readonly>

測試範例 15 : http://tony1966.xyz/test/jquerytest/datepicker_15.htm [看原始碼]

38 則留言 :

Unknown 提到...

謝謝你的分享XD
不過有辦法可以做到不選已過的日期嗎

小狐狸事務所 提到...

您好, 當然可以呀, 只要將 minDate 選項設為今日日期即可 :
minDate: new Date()

Unknown 提到...

謝謝你的分享
想請教如果星期一與星期四兩欄的顏色與其他日期的顏色不同要怎麼改寫

Unknown 提到...

還有,如果我有設定每天的截止時間,只要超過截止時間,今天日期就不列入選擇日期,只能改選明天以後的日期,這可以怎麼做呢?

小狐狸事務所 提到...

戴兄, 所問回答如上, 請參考.

Unknown 提到...

感謝版主熱情的分享,本以為就此沒下文了說,今個兒要好好的來研究研究,再次感謝.

Unknown 提到...

成了,兩個合起來終於是我想要的東西,謝謝你的分享

Unknown 提到...

Tony兄,小弟我學藝不精,是否可以請教另外的問題:
問題一.
如果說一個人做工一小時的工資為50元,兩個人同時做工一小時,必須均分這50元的工資,變成每人每小時分得的工資剩為25元,三人同時做工一小時的工資必須把50元均分為三等份,以此類推,但是均分的工資必須為整數才成立,且不得為小數點以下,只要所分得的工資有小數點就跳過參予的人數,還有每人每天的工資不得低於5元,如果做兩天,每人每天的工資不得低於10元,如果做三天,每人每天的工資不得低於15元,以此類推,那我可以自由選擇來做1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 or 10 or 15 or 20個小時的工,並可以自由選擇與1~20個人(最多20人)一起來做工,然後能即時的看到自動計算所分得的錢.
問題二.
(承問題一.)已經選定了工時和一起做的人數,但是做工超時了,超過1小時就給1倍的工資,超過2小時就給2倍的工資,超過3小時就給3倍的工資,以此類推,頂多超過10小時,然後也能即時的看到自動計算所分得的錢.


Unknown 提到...

版主你好:
想再請教日期選取的問題:
如果我已經選取了日期,如何防止手動修改成錯誤的日期(包含不存在的日期)或是只能由選取器來選擇日期??有勞

yuching 提到...

您好:
如果要在前一頁,利用checkbox做勾選資料的動作,再把勾選的資料每一筆依序選擇日期,不知道要怎麼改寫code?

小狐狸事務所 提到...

不太理解您的提問, 前一頁 checkbox 是用來勾選日期嗎?

yuching 提到...

我同時勾選兩筆資料,這兩筆資料都有一個要選擇日期的欄位,要怎樣讓這兩筆資料,分別可以利用日期選擇器,選擇日期並寫入文字欄位裡

號子 提到...

請問
要怎麼把datepicker整個變小
他原本的字太大了
和我做的網頁差太多
謝謝

小狐狸事務所 提到...

如果沒有任何樣式設定確實蠻大的, 您可以試試在 head 中加個如下的樣式 :
body {font: 62.5%}
這樣就會全體字型都縮小為 62.5% 了.

號子 提到...

不好意思
加到head裡面是指哪邊的head?
因為我加到網頁的head後
他變成只有把body {font: 62.5%}顯示出來
然後我換成font size = 62.5%
結果全部的字都變大了
但是datepicker的字還是沒變

小狐狸事務所 提到...

請參考本文的每一個範例, 都有如下設定 :


body {font: 62.5% "Trebuchet MS", sans-serif; margin: 50px;}

樣式要用 style 標籤包起來.

匿名 提到...

謝謝分享,請問有辦法做到,指定的幾個日期是使用者無法點選。謝謝~

匿名 提到...

minDate 是否只能設定一次
$(function () {

$('#dtbegin').datepicker({ dateFormat: "yy/mm/dd" });

$('#dtend').datepicker({ dateFormat: "yy/mm/dd" });

$('#dtbegin').bind('change', function () {
var tdate = $("#dtbegin").datepicker("getDate");
tdate = $.datepicker.formatDate("yy/mm/dd", tdate);
$("#dtend").datepicker({ dateFormat: "yy/mm/dd", minDate: tdate });

});

});

結果可選取最小日期總是停留在選取起始日的第一次...

Unknown 提到...

超級詳細~~受用無窮啊!!!感恩

匿名 提到...

您好:
謝謝您的整理

我把這幾個 css、js 都下載到pc上

並修改 html 如下

link href="jquery-ui.css" rel="stylesheet"
script type="text/javascript" src="jquery.min.js"
script type="text/javascript" src="jquery-ui.min.js"

功能都OK

但上一月 、 上一月的箭頭都不見了,請問這是?

謝謝您

小狐狸事務所 提到...

前陣子丁母憂, 因此無暇回覆留言, 今日有匿名者問, 為何將範例中的 js 下載後, 功能正常, 但但上一月 上一月的箭頭都不見了, 這是因為除了這些外, 還需要 themes 的關係. 請參考 jQueryUI 環境配置這篇, 下載 ZIP 檔或使用 CDN 均可 :

http://yhhuang1966.blogspot.tw/2013/03/jquery-ui.html

旅遊小K 提到...

HELLO Tony
您好: 我是一名Jquery UI新手,請問要如何使datepicker 正常顯示呢 我將"原始碼" 貼至 自訂HTML
只有input 欄位卻不會有日曆出現
是不是我什麼地方少做了呢

小狐狸事務所 提到...

Dear Leic, 您需要配置一下執行環境, 原始碼中 head 內有 js 與 css 資源, 這些都需要準備好, 否則將無法呈現效果. 我在自己的測試伺服器有配置全部 jQuery UI 環境, 如果不想自行配置, 可採用 CDN 所提供之環境, 參考這篇 :

http://yhhuang1966.blogspot.tw/2013/03/jquery-ui.html

pan 提到...

您好,請教您,我想在輸入欄位先顯示預設日期,所以我加上value="2015-01-01" 如下,
input id="datepicker1" type="text" value="2015-01-01"
但,一直無法顯示出來,請問您在使用datepicker時,曾遇過此狀況嗎?謝謝。

pan 提到...

您好,我已經範例9找到解答了,非常謝謝您無私的分享 $("#datepicker1").datepicker("setDate",new Date(2013,3,1));

匿名 提到...

你好~我想請教您:
如果我使用datetimepicker同時也提供使用者在input中自己key in,這樣如果使用者輸入小於9的時間時,如何使datetimepicker自動補0呢?(時間格式yyyy/mm/dd hh:ii)

匿名 提到...

你好~想請問一下,dafaultDate這個功能好像沒有起作用
見你的範例六也有這樣的情況,是甚麼原因呢?
謝謝

小狐狸事務所 提到...

嗯, 確實如此, 但當初測試時是 ok 的, 有空我研究看看, 感謝您.

Unknown 提到...

版大您好!很感謝你的無私分享!
想請問一個問題,日曆能否只顯示每個月的一號讓使用者點選呢?
其餘的日期都是灰色無法點選的狀態!

Unknown 提到...

感謝分享

學了一堂課

匿名 提到...

感謝分享

Unknown 提到...

請問可否設定一定要選該日期才能送出呢?
不能空白就輸出?

Unknown 提到...

請問如何讓只能點選日期
不能去手動修改日期
試過將input放入 readonly="readonly"
發現不能打入 但是卻可以刪除部分日期
還是可以錯誤的送出
例如
2017-0
2017-05-2
這些不完整的日期

小狐狸事務所 提到...

參考範例 15, 設 readonly 之後就只能靠點選設定日期, 沒辦法按 delete 刪除部分資訊喔.

Unknown 提到...
作者已經移除這則留言。
Unknown 提到...

請問,月份下拉選單的格式,可以自訂嗎?!
意思是,目前預設是顯示 Jan,Feb....,能否改為 一月,二月...
謝謝~
p.s.試過monthNames,但對下拉選單無效

匿名 提到...

如果要全選日期或取消全選日期 要如何寫呢?謝謝

小狐狸事務所 提到...

Hi, 日期選取器框內只能有一個被選取日期喔!