本文介紹基於 Dynarch DHTML Calendar (v1.0) 的前後端整合方案。雖該庫停止更新多年,但其零依賴、LGPL 協議、完善的鍵盤導航、時區處理、多語言支持使其在存量系統維護、內網項目、輕量級場景中仍具不可替代價值。現代項目建議優先考慮 flatpickr、Datepicker.js 或框架組件庫。
┌─────────────────────────────────────┐
│ PHP 後端渲染層 │
│ - disp_js_calendar() 輔助函數 │
│ - 動態輸出 JS 初始化配置 │
└──────────────┬──────────────────────┘
│ HTML + Inline JS Config
▼
┌─────────────────────────────────────┐
│ 瀏覽器運行時 │
│ - calendar.js 核心邏輯 (4000+ 行) │
│ - calendar-setup.js 便捷 API │
│ - calendar-big5-utf8.js 語言包 │
│ - CSS 主題樣式 │
└──────────────┬──────────────────────┘
│ DOM Events / Callbacks
▼
┌─────────────────────────────────────┐
│ 用戶交互層 │
│ - 輸入框 + 觸發按鈕 │
│ - Popup / Flat 兩種顯示模式 │
│ - 日期選擇回填 │
└─────────────────────────────────────┘
<?php
/**
* 輸出日歷初始化腳本
* @param string $button_id 觸發按鈕 ID
* @param string $output_id 日期輸入框 ID
*/
function disp_js_calendar($button_id, $output_id) {
echo <<<JS
<script type="text/javascript">
Calendar.setup({
inputField : "$output_id", // 目標輸入框 ID
ifFormat : "%Y-%m-%d", // 存儲格式
showsTime : false, // 是否顯示時間
button : "$button_id", // 觸發按鈕 ID
singleClick: true, // 單擊選中
step : 1 // 年份步長
});
</script>
JS;
}
// 使用示例
echo '<input type="text" name="src_date" id="src_date" value="'.htmlspecialchars($_POST['src_date'] ?? '').'">';
echo '<input type="button" id="BTN" value="📅">';
disp_js_calendar("BTN", "src_date");
?>
Calendar.setup() 參數全景| 參數 | 類型 | 默認值 | 說明 |
|---|---|---|---|
| inputField | HTMLElement/String | null | 日期輸入框 DOM 或 ID |
| displayArea | HTMLElement/String | null | 替代顯示區域(非輸入框) |
| button | HTMLElement/String | null | 觸發元素,必填其一 |
| eventName | String | "click" | 觸發事件名 |
| ifFormat | String | "%Y/%m/%d" | 輸入框存儲格式 (strftime) |
| daFormat | String | "%Y/%m/%d" | displayArea 顯示格式 |
| singleClick | Boolean | true | 單擊關閉/雙擊關閉 |
| firstDay | Number | 0 | 週首日 0=Sun, 1=Mon |
| align | String | "Br" | 定位對齊:TL/TR/BL/BR/C |
| range | Array | [1900,2999] | 年份範圍 |
| weekNumbers | Boolean | true | 顯示週數 |
| flat | HTMLElement/String | null | 內嵌模式父容器 |
| disableFunc | Function | null | 禁用日期判斷函數 |
| onSelect | Function | 內置 | 選中回調 |
| showsTime | Boolean | false | 包含時間選擇器 |
| timeFormat | String | "24" | "12" 或 "24" |
| electric | Boolean | true | 實時更新/關閉時更新 |
| cache | Boolean | false | 複用日歷實例 |
| showOthers | Boolean | false | 顯示鄰月日期 |
Calendar.setup({
// ...
disableFunc: function(date) {
var today = new Date(); today.setHours(0,0,0,0);
return date < today; // 禁用過去日期
}
});
<div id="cal-container"></div>
<script>
Calendar.setup({
flat: "cal-container",
flatCallback: function(cal) {
var url = "/archive/" + cal.date.print("%Y/%m/%d");
window.location.href = url;
}
});
</script>
Calendar.setup({
// ...
multiple: true, // 需配合 flat 模式
onSelect: function(cal) {
console.log('已選日期:', Object.keys(cal.multiple));
}
});
calendar-big5-utf8.js 關鍵點語言包定義了 Calendar._DN (星期), Calendar._MN (月份), Calendar._TT (工具提示) 等常量。注意編碼必須為 UTF-8,且頁面 <meta charset="utf-8"> 一致,否則會出現亂碼。
原 CSS 大量硬編碼顏色、圖片路徑。現代化改造:
:root {
--cal-bg: #fff;
--cal-border: #ddd;
--cal-header-bg: #f5f5f5;
--cal-today-bg: #e8f5e9;
--cal-selected-bg: #1976d2;
--cal-selected-color: #fff;
--cal-weekend-color: #c62828;
}
div.calendar { background: var(--cal-bg); border-color: var(--cal-border); }
.calendar .button { background: var(--cal-header-bg); }
.calendar tbody td.today { background: var(--cal-today-bg); }
.calendar tbody td.selected { background: var(--cal-selected-bg); color: var(--cal-selected-color); }
.calendar thead .weekend, .calendar tbody td.weekend { color: var(--cal-weekend-color); }
將圖片背景 (title-bg.gif 等) 替換為 CSS 漸變/陰影,實現零圖片、高性能、易主題化。
| 現象 | 原因 | 解決 |
|---|---|---|
| 日歷不顯示/位置錯誤 | CSS 衝突 / 父元素 overflow:hidden | 檢查 z-index、定位上下文、引入順序 |
| 日期格式不匹配 | ifFormat/daFormat 與 PHP date() 格式混淆 | 統一使用 strftime 格式 (%Y-%m-%d) |
| 中文亂碼 | JS 文件編碼/頁面編碼/HTTP Header 不一致 | 全鏈路 UTF-8,JS 文件保存為 UTF-8 無 BOM |
| IE 下報錯 | 舊版庫使用 attachEvent/document.all | 引入 Polyfill 或升級現代替代庫 |
| 觸發按鈕無反應 | ID 拼寫錯誤 / 元素未就緒時綁定 | 確保 DOM Ready 後調用 setup() |
新項目強烈推薦:
npm i flatpickr,30KB 無依賴,TypeScript 原生,功能豐富<input type="date">:無依賴,語義化,移動端體驗佳,配合 @datepicker-polyfill 兼容舊瀏覽器Dynarch Calendar 承載了 Web 1.0 時代的輝煌。理解其配置驅動、回調擴展、語言包分離的設計思想,對掌握現代組件庫架構大有裨益。維護舊系統時,本文提供的整合模式與坑點指南可助你「穩住陣腳」;新項目請毫不猶豫選擇現代方案——站在巨人的肩膀上,而不是巨人的腳印裡。