我想要寫一個可以生成整學期行事曆的html工具,要可以讓我輸入行事曆標題,設定從那一週開始,生成幾週的行事曆,行事曆第一欄要是月份,第二到八欄為日、一、二到六的日期,其中第二及第八欄的單元格背景色用淡灰色。第九欄為學校行事,第十欄為備註,1-8欄為佔用整個表格寬度的一半,9欄佔另一半寬度的4分之3,剩下的寬度為第10欄,生成的畫面上有一個下載行事曆的按鈕,按下後,可以把生成的行事曆下載成html的doc檔。整體介面要好看一點
推理關於行事曆生成工具花了 28 秒
以下是一個完整的 HTML 範例程式碼,包含 CSS 與 JavaScript,可讓你依需求輸入行事曆標題、設定起始日期與生成週數,並依照格式產生一個符合規格的行事曆,畫面上還有一個按鈕可以下載生成的 HTML 檔案。你可以直接將下列程式碼存成一個 .html 檔,再用瀏覽器開啟即可體驗效果。
下面程式碼說明與重點整理:
輸入介面:包含行事曆標題、起始日期(建議選擇該週的「日」)及生成幾週的行事曆。
表格結構:
第一欄顯示當週(依起始日期的「日」)所屬的月份。
第二到第八欄分別為「日、一、二、三、四、五、六」,其中第2欄與第8欄背景色為淡灰色(可自行調整 CSS)。
第九欄為學校行事。
第十欄為備註。
寬度設定:整個表格分為兩大區塊,1~8欄共佔 50% 寬度,剩下 50% 再依比例分為第9欄(75% ≒ 37.5%整體)與第10欄(25% ≒ 12.5%整體)。
下載功能:按下「下載行事曆」按鈕後,將把當前畫面生成的 HTML 當成檔案下載。
你也可以依需求進一步美化介面,下面程式碼供參考:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 900px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
.calendar-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.calendar-table th, .calendar-table td {
border: 1px solid #ccc;
padding: 8px;
text-align: center;
}
/* 設定第2欄與第8欄背景淡灰 */
.calendar-table td:nth-child(2),
.calendar-table td:nth-child(8) {
background-color: #f0f0f0;
}
/* 使用 colgroup 設定表格各欄寬度 */
.calendar-table colgroup col {
/* 預設設定,但實際比例在 generate 時以 inline style 設定 */
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
<div id="calendarContainer"></div>
<button id="downloadBtn" style="display: none;" onclick="downloadCalendar()">下載行事曆</button>
</div>
<script>
// 取得中文星期名稱
const weekDays = ['日','一','二','三','四','五','六'];
// 根據起始日期與週數生成行事曆表格
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
// 開始組裝 HTML 內容(包含行事曆標題與表格)
let htmlContent = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 8px; text-align: center; }
td:nth-child(2), td:nth-child(8) { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>${title}</h1>
<table>
<colgroup>
<col style="width:6.25%;"> <!-- 月份,佔整體寬度 1/16 (6.25%) -->
<col style="width:6.25%;">
<col style="width:6.25%;">
<col style="width:6.25%;">
<col style="width:6.25%;">
<col style="width:6.25%;">
<col style="width:6.25%;">
<col style="width:6.25%;"> <!-- 共8欄約50% -->
<!-- 第二塊欄位共50% -->
<col style="width:37.5%;"> <!-- 第9欄:學校行事(75% 的 50%) -->
<col style="width:12.5%;"> <!-- 第10欄:備註(25% 的 50%) -->
</colgroup>
<thead>
<tr>
<th>月份</th>
<!-- 日 ~ 六 -->
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 產生每週的資料列
let currentDate = new Date(startDate);
for (let i = 0; i < weeksCount; i++) {
// 以每週第一天 (即選擇的那一天,通常為 Sunday) 的月份為該列的月份
const month = currentDate.getMonth() + 1;
htmlContent += `<tr>`;
htmlContent += `<td>${month}月</td>`;
// 產生一週中 7 天(日 ~ 六)
for (let d = 0; d < 7; d++) {
const dayNum = currentDate.getDate();
htmlContent += `<td>${dayNum}</td>`;
// 移動至下一天
currentDate.setDate(currentDate.getDate() + 1);
}
// 兩個額外欄位:學校行事與備註 (預設空白)
htmlContent += `<td></td><td></td>`;
htmlContent += `</tr>`;
}
htmlContent += `
</tbody>
</table>
</body>
</html>
`;
// 將生成的行事曆 HTML 顯示於頁面上(可以在 container 中嵌入 iframe 或 pre 標籤展示)
document.getElementById('calendarContainer').innerHTML =
`<h2>${title}</h2><iframe style="width:100%; height:500px; border:1px solid #ccc;" srcdoc="${htmlContent.replace(/"/g, '"')}"></iframe>`;
// 儲存於全域變數中,以便下載時使用
window.generatedCalendarHTML = htmlContent;
// 顯示下載按鈕
document.getElementById('downloadBtn').style.display = 'inline-block';
}
// 將生成的 HTML 作為 .doc 檔案下載
function downloadCalendar() {
if (!window.generatedCalendarHTML) {
alert("請先生成行事曆!");
return;
}
// 利用 Blob 與 a 元素建立下載連結
const blob = new Blob([window.generatedCalendarHTML], {type: "text/html"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
// 設定檔案名稱
a.download = "學期行事曆.doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// 釋放 URL 物件
URL.revokeObjectURL(url);
}
</script>
</body>
</html>
介面表單
使用 <form>
收集使用者輸入,包括行事曆標題、起始日期(建議選擇該週的「日」,方便計算)與生成的週數。
日曆生成邏輯
JavaScript 中 generateCalendar()
函數會依照輸入的起始日期、週數生成整個行事曆的 HTML。
表格的第一欄顯示月份(這裡採用每週第一天的月份),接下來七欄分別是「日、一、二、三、四、五、六」,最後兩欄則保留供學校行事與備註。
CSS 中利用 colgroup
設定各欄的寬度,滿足前 8 欄佔 50%,再由第9欄佔 75% 與第10欄佔 25%(分別為 37.5% 與 12.5%)。
下載功能
點擊「下載行事曆」按鈕後,downloadCalendar()
會將前面生成的 HTML 內容轉成 Blob,並以 .doc 檔案格式下載。
介面美化
使用簡單的 CSS 效果美化頁面(例如容器的陰影、圓角、按鈕的 hover 效果等),使介面整體更好看。
這份程式碼提供一個基礎範例,你可以依照需求再進一步調整細節(例如各欄位進一步格式化、處理週跨月份的情形等等)。希望這能滿足你的需求!
1.生成的行事曆最左方加上一欄"週次",會呈現出數字的週次 2.月份的欄位,同月的單元格合併成一格來呈現。 3.週次及星期幾的欄位,文字大小設為10PX,表格寬度改為佔用全寬的三分之一就好, 剩下的三分之二寬度,學校行事佔4分之3,備註佔4分之1。
推理花了 1m 34s
下面提供一個更新後的完整 HTML 程式碼,除了原本的功能外,根據你的需求做了以下調整:
增加「週次」欄位:最左邊新增一欄,顯示每週的編號。
月份欄位合併:若連續多行顯示同一月份,將月份欄位使用 rowspan 合併成一格。
調整欄位比例與字體大小:
「週次」以及「星期(日、一、二、三、四、五、六)」欄位(共8欄)的文字設定為 10px
整個表格分成左右兩大區塊:
左側(包含「週次」、「月份」及「日~六」共 9 欄)寬度設定為全寬的三分之一
右側(「學校行事」與「備註」)佔剩下的三分之二,其中「學校行事」佔 4/3(即 75%)、「備註」佔 1/4(即25%)
以下程式碼將這些需求整合在一起,直接存成 .html 檔後於瀏覽器執行即可預覽與下載產生的行事曆。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
.calendar-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.calendar-table th, .calendar-table td {
border: 1px solid #ccc;
padding: 6px;
text-align: center;
}
/* 設定週次及星期幾欄位文字大小 10px */
.small-text {
font-size: 10px;
}
/* 設定第2欄(月份)背景不另外設定,若有需要可調整 */
/* 設定淡灰色背景,如原本第二與第八欄(由於現在欄位順序有變,星期的日與六欄改成第?欄) */
/* 本範例僅針對原先設定的淡灰色,這裡採用星期「日」與「六」 */
.gray-bg {
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
<div id="calendarContainer"></div>
<button id="downloadBtn" style="display: none;" onclick="downloadCalendar()">下載行事曆</button>
</div>
<script>
// 星期名稱
const weekDays = ['日','一','二','三','四','五','六'];
// 根據起始日期與週數生成行事曆,包含週次、月份合併、日期
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 產生每週資料 (週次, 月份, 7天日期)
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 開始組裝日曆 HTML,採用 colgroup 設定欄寬:
// 左側區塊:共 9 欄(週次、月份、7個星期)佔 33.33%(各欄平均約3.7%)
// 右側區塊:學校行事佔 66.67%*75% = 50%,備註佔 16.67%
let htmlContent = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 6px; text-align: center; }
/* 左側區塊:週次及星期欄位文字大小 10px */
.small-text { font-size: 10px; }
/* 淡灰色背景,僅針對星期「日」與「六」 */
.gray-bg { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>${title}</h1>
<table>
<colgroup>
<!-- 左側 9 欄均分 33.33% -->
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<col style="width:3.7%;">
<!-- 右側兩欄 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th class="small-text">週次</th>
<th>月份</th>
${weekDays.map(day => {
// 為「日」與「六」設定淡灰色背景
if(day === "日" || day === "六"){
return `<th class="small-text gray-bg">${day}</th>`;
} else {
return `<th class="small-text">${day}</th>`;
}
}).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 處理月份合併:使用一個旗標陣列記錄哪些列已顯示過月份
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
htmlContent += `<tr>`;
// 週次欄
htmlContent += `<td class="small-text">${rowsData[i].week}</td>`;
// 月份欄:只有該月份第一筆才輸出,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
htmlContent += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 星期(7天)欄位:為「日」與「六」加上淡灰色背景與 10px 字體
for (let d = 0; d < 7; d++) {
let tdClass = "small-text";
if(d === 0 || d === 6) { // 日與六
tdClass += " gray-bg";
}
htmlContent += `<td class="${tdClass}">${rowsData[i].days[d]}</td>`;
}
// 右側兩欄:學校行事與備註,預設空白
htmlContent += `<td></td><td></td>`;
htmlContent += `</tr>`;
}
htmlContent += `
</tbody>
</table>
</body>
</html>
`;
// 將生成的日曆 HTML 置於頁面上(利用 iframe 呈現)
document.getElementById('calendarContainer').innerHTML =
`<h2>${title}</h2><iframe style="width:100%; height:500px; border:1px solid #ccc;" srcdoc="${htmlContent.replace(/"/g, '"')}"></iframe>`;
// 儲存全域變數,供下載使用
window.generatedCalendarHTML = htmlContent;
// 顯示下載按鈕
document.getElementById('downloadBtn').style.display = 'inline-block';
}
// 下載生成的 HTML (以 .doc 檔案副檔名)
function downloadCalendar() {
if (!window.generatedCalendarHTML) {
alert("請先生成行事曆!");
return;
}
const blob = new Blob([window.generatedCalendarHTML], {type: "text/html"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "學期行事曆.doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
</script>
</body>
</html>
資料結構
利用 JavaScript 先建立一個 rowsData
陣列,每筆記錄包含該週的編號(週次)、該週的月份(用於合併欄位)與7天的日期。
月份合併
利用 skipMonth
陣列記錄同月連續列是否已輸出月份欄,若是該筆為同月的第一個,計算接下來連續相同月份的列數作為 rowspan。
欄位寬度配置
使用 <colgroup>
將表格分為兩個區塊:
左側共 9 欄(包含「週次」、「月份」及 7 個星期)的總寬度設為 33.33%
右側兩欄分別設定為 50% 與 16.67%(滿足 75%/25% 的比例)
字體與背景
利用 CSS 類別 small-text
將「週次」以及「星期」欄位字體設為 10px;同時對星期「日」與「六」欄位加入 gray-bg
類別套用淡灰背景色。
下載功能
點擊下載按鈕後,將生成的 HTML 利用 Blob 機制轉為檔案下載,檔名預設為「學期行事曆.doc」。
這份程式碼結合你的所有需求,供你參考與進一步調整。希望能幫助你順利完成學期行事曆工具的製作!
1.在生成的行事曆上,使用者點選一二三四五六日單元格時,該單元格背景色會變成日及六的單元格的背景色,再點一下會回複成無背景色。 2.按下生成行事曆,在新的圖層開啟生成的行事曆,下載行事曆的按鈕也改放在這個圖層上。 3.下載的行事曆要包含單元格的淡灰色背景的設定
推理花了 1m 16s
以下提供更新後的完整 HTML 程式碼,整合了你最新提出的需求,說明如下:
單元格點選切換背景色
生成行事曆的「星期」單元格(包含一、二、…、六、日)都加上 class "day-cell"
,並依據原本的規則:
綁定點選事件:
點選後若該 cell 的 inline backgroundColor 為 #f0f0f0(瀏覽器呈現會是 rgb(240, 240, 240)),則移除 inline style;
否則設定為淡灰色。
行事曆生成後以新圖層(Modal)開啟
下載的行事曆檔案包含格子背景設定
請依需求儲存以下程式碼為 .html 檔後,即可直接用瀏覽器執行。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 於行事曆中 left block(週次、月份、星期欄)佔 1/3,全表格寬度 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 左側區塊中的欄位文字設為 10px */
.small-text {
font-size: 10px;
}
/* day-cell 用來切換背景 */
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 當前產生的完整 HTML 存檔 (供下載使用)
let fullCalendarHTML = "";
// 點選後切換背景色:若目前為 #f0f0f0(即 rgb(240,240,240)),則移除;否則設為 #f0f0f0。
function toggleCellBackground(event) {
const cell = event.currentTarget;
// 取得目前設定的背景色(瀏覽器通常回傳 rgb 格式)
const currentColor = cell.style.backgroundColor;
if (currentColor === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆:處理資料、組成 HTML(同時產生供 Modal 顯示以及下載的完整檔案)
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 依週產生資料:紀錄週次、該週的月份(用於合併同月)與 7 天的日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組成下載用的完整 HTML(包含 DOCTYPE、head、style、script)
fullCalendarHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 6px; text-align: center; }
.small-text { font-size: 10px; }
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<h1>${title}</h1>
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7個星期,每欄 33.33%/9 約 3.70% -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事 (75% of 2/3=50%) 與 備註 (25% of 2/3=16.67%) -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th class="small-text">週次</th>
<th>月份</th>
${weekDays.map(day => `<th class="small-text">${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 為處理同月合併,使用旗標陣列記錄哪些列已經產生過月份儲存格
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
fullCalendarHTML += `<tr>`;
// 週次欄
fullCalendarHTML += `<td class="small-text">${rowsData[i].week}</td>`;
// 月份欄:僅在該月份第一筆產出,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
fullCalendarHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 星期欄(7 天):點選後切換背景
for (let d = 0; d < 7; d++) {
// 若該欄位為「日」(d==0)或「六」(d==6),預設內嵌 style 設定淡灰色背景
if(d === 0 || d === 6) {
fullCalendarHTML += `<td class="day-cell small-text" data-default="#f0f0f0" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
fullCalendarHTML += `<td class="day-cell small-text" data-default="#f0f0f0">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註(預設空白)
fullCalendarHTML += `<td></td><td></td>`;
fullCalendarHTML += `</tr>`;
}
fullCalendarHTML += `
</tbody>
</table>
<script>
// 為所有具有 .day-cell 類別的單元格加入點選切換背景色功能
document.querySelectorAll('.day-cell').forEach(cell => {
cell.addEventListener('click', function() {
if (this.style.backgroundColor === "rgb(240, 240, 240)") {
this.style.backgroundColor = "";
} else {
this.style.backgroundColor = "#f0f0f0";
}
});
});
<\/script>
</body>
</html>
`;
// 組成供 Modal 顯示的內容(這裡只顯示行事曆的 table 區塊)
let modalHTML = `
<h2>${title}</h2>
${fullCalendarHTML.substring(fullCalendarHTML.indexOf('<table'), fullCalendarHTML.indexOf('</html>'))}
<script>
// 為 Modal 內的 day-cell 加入點選事件 (不影響下載檔案內的事件)
document.querySelectorAll('.day-cell').forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
<\/script>
`;
// 將 Modal 顯示內容放入 #calendarDisplay 區塊
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載生成的行事曆(完整 HTML 檔案)
function downloadCalendar() {
if (!fullCalendarHTML) {
alert("請先生成行事曆!");
return;
}
const blob = new Blob([fullCalendarHTML], {type: "text/html"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "學期行事曆.doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
// 為 Modal 內的 day-cell 設定點選事件(切換背景色)
document.addEventListener('click', function(e) {
if(e.target && e.target.classList.contains('day-cell')){
toggleCellBackground(e);
}
});
</script>
</body>
</html>
單元格互動
每個日期單元格皆加入 class="day-cell"
,若為「日」或「六」則預設 inline style 背景色設定為 #f0f0f0
;點選時會利用 JavaScript 檢查目前背景色,進而在「無背景」與「淡灰色」間切換。
Modal 彈出圖層
當點選「生成行事曆」後,利用隱藏的 #modalOverlay
區塊顯示生成的行事曆及下載/關閉按鈕。Modal 內容中也加入了對單元格點選事件的綁定,確保在彈出圖層內可執行切換功能。
下載檔案完整性
下載的行事曆檔案(副檔名 .doc)採用完整的 HTML 格式,包含 head 區段的 CSS 設定(確保各單元格背景色也能顯示),以及點選切換背景的 JavaScript。
這份程式碼結合了所有需求,可直接使用、修改,並依據實際需要進行微調。希望這能滿足你的需求!
Uncaught TypeError: Cannot read properties of undefined (reading 'backgroundColor')
at toggleCellBackground (行事曆生成AI示範.html:134:39)
at HTMLDocument.
Reasoned about background color toggle issue花了 19 秒
下面提供修正後的程式碼範例,重點改動如下:
修改了點選切換背景色的函式,改用 event.target 來取得點選的儲存格,避免因文件層級監聽器造成 currentTarget 為 undefined 的問題。
移除了最下方「document.addEventListener('click', …)」的全域點選監聽器,改由生成 HTML 時就各自加入點選事件,這樣能確保每個 .day-cell 都正確綁定。
請將下列程式碼存成 .html 檔後,直接用瀏覽器開啟即可運作。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
.small-text {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 全域變數,用於儲存生成的完整 HTML (供下載使用)
let fullCalendarHTML = "";
// 點選切換背景色
function toggleCellBackground(event) {
// 使用 event.target 取得點選的儲存格
const cell = event.target;
if (cell.style.backgroundColor === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,組成供 Modal 顯示與下載的完整檔案內容
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 依週產生資料:紀錄週次、月份(用於合併儲存格)與7天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 生成下載用完整 HTML (包含 head, style, script)
fullCalendarHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 6px; text-align: center; }
.small-text { font-size: 10px; }
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<h1>${title}</h1>
<table>
<colgroup>
<!-- 左側 9欄:週次、月份、7個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th class="small-text">週次</th>
<th>月份</th>
${weekDays.map(day => `<th class="small-text">${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些行的月份欄位已輸出
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
fullCalendarHTML += `<tr>`;
// 週次
fullCalendarHTML += `<td class="small-text">${rowsData[i].week}</td>`;
// 月份欄:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
fullCalendarHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生7天日期,加入點選事件 (日與六預設淡灰色)
for (let d = 0; d < 7; d++) {
if(d === 0 || d === 6) {
fullCalendarHTML += `<td class="day-cell small-text" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
fullCalendarHTML += `<td class="day-cell small-text">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註 (預設空白)
fullCalendarHTML += `<td></td><td></td>`;
fullCalendarHTML += `</tr>`;
}
fullCalendarHTML += `
</tbody>
</table>
<script>
// 為所有 day-cell 加入點選事件,點選後背景色在無與淡灰之間切換
document.querySelectorAll('.day-cell').forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
<\/script>
</body>
</html>
`;
// 組成供 Modal 顯示的部分,這裡只擷取 <table> ... </table> 區塊
const tableStart = fullCalendarHTML.indexOf('<table');
const tableEnd = fullCalendarHTML.indexOf('</table>') + 8; // 8 為 '</table>'.length
let modalHTML = `<h2>${title}</h2>`;
modalHTML += fullCalendarHTML.substring(tableStart, tableEnd);
// Modal 內亦需啟用切換事件,因此在 Modal 內加入 script
modalHTML += `
<script>
document.querySelectorAll('.day-cell').forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
<\/script>
`;
// 放入 Modal 內容中
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載生成的行事曆 (完整 HTML 檔案)
function downloadCalendar() {
if (!fullCalendarHTML) {
alert("請先生成行事曆!");
return;
}
const blob = new Blob([fullCalendarHTML], {type: "text/html"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "學期行事曆.doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
切換背景色功能
在函式 toggleCellBackground 中改用 event.target(即被點選的那個儲存格)來讀取或設定 style.backgroundColor,這樣就不會因為 event.currentTarget 指向文件而導致 undefined 的錯誤。
各儲存格事件綁定
在生成完整 HTML 時及 Modal 顯示時,對每個擁有 class "day-cell" 的儲存格皆使用 addEventListener 綁定 toggleCellBackground。
這份修正後的程式碼應該能解決 Uncaught TypeError 的錯誤,請試試看並依需求作進一步調整!
點了日一二三四五六的欄位中的單元格,沒有出現切換背景色的效果
推理花了 17 秒
以下提供修正後的完整範例,調整重點包括:
事件綁定:
由於將含有 <script>
的內容以 innerHTML 插入時不會自動執行其中的 JavaScript,因此改在將 Modal 區塊注入後,利用程式碼一次性對 Modal 區塊內所有 .day-cell 元素進行事件綁定。
背景色切換判斷:
仍採用點選後讀取元素的 inline backgroundColor,如果背景色為淡灰色(rgb(240, 240, 240))則清除,否則設定為淡灰色。
請試試下列程式碼:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
.small-text {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 全域變數,用於儲存生成的完整 HTML (供下載使用)
let fullCalendarHTML = "";
// 點選切換背景色
function toggleCellBackground(event) {
const cell = event.target;
// 使用 getComputedStyle 取得最終背景色的 rgb 表示
const currentBg = window.getComputedStyle(cell).backgroundColor;
// 如果目前是淡灰色 (rgb(240, 240, 240)) 則清除,否則設為淡灰色
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,組成供 Modal 顯示與下載的完整檔案內容
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 每週資料:包含週次、月份(用於合併儲存格)以及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 生成下載用完整 HTML (包含 head, style, script)
fullCalendarHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 6px; text-align: center; }
.small-text { font-size: 10px; }
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<h1>${title}</h1>
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th class="small-text">週次</th>
<th>月份</th>
${weekDays.map(day => `<th class="small-text">${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些列已輸出月份儲存格(用於合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
fullCalendarHTML += `<tr>`;
// 週次
fullCalendarHTML += `<td class="small-text">${rowsData[i].week}</td>`;
// 月份儲存格:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
fullCalendarHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生 7 天欄位,若為日(d==0)或六(d==6)則預設淡灰背景
for (let d = 0; d < 7; d++) {
if(d === 0 || d === 6) {
fullCalendarHTML += `<td class="day-cell small-text" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
fullCalendarHTML += `<td class="day-cell small-text">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄
fullCalendarHTML += `<td></td><td></td>`;
fullCalendarHTML += `</tr>`;
}
fullCalendarHTML += `
</tbody>
</table>
<script>
// 為完整文件內所有 .day-cell 加入切換背景事件
document.querySelectorAll('.day-cell').forEach(cell => {
cell.addEventListener('click', ${toggleCellBackground.toString()});
});
<\/script>
</body>
</html>
`;
// 產生用於 Modal 顯示的內容:這裡只取出 <table> ... </table> 區塊,
// 注意:由 innerHTML 載入的 script 不會自動執行,所以需在 Modal 注入後重新綁定事件
const tableStart = fullCalendarHTML.indexOf('<table');
const tableEnd = fullCalendarHTML.indexOf('</table>') + 8;
let modalHTML = `<h2>${title}</h2>`;
modalHTML += fullCalendarHTML.substring(tableStart, tableEnd);
// 將 Modal 內容更新進 #calendarDisplay
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// 於 Modal 內容更新後,對所有 day-cell 重新綁定事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載生成的行事曆 (完整 HTML 檔案)
function downloadCalendar() {
if (!fullCalendarHTML) {
alert("請先生成行事曆!");
return;
}
const blob = new Blob([fullCalendarHTML], {type: "text/html"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "學期行事曆.doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
在函式 toggleCellBackground 中改用 window.getComputedStyle 讀取元素最終的背景顏色,確保判斷正確。
當 Modal 內容以 innerHTML 注入後,立即使用 querySelectorAll 找出 modal 內所有 .day-cell,並對它們依次綁定 toggleCellBackground 事件。
這樣點選「日、一、二、...」的儲存格時,背景色就會在預設的淡灰 (#f0f0f0) 與無背景之間切換。請試試看此版本,應該能正常達成需求。
1.下載行事曆,要包含點選單元格後的背景色變化,下載的是下載當下的表格情形。 2.下載的檔名,請用輸入的標題來當檔名
推理花了 27 秒
下方提供修改後的完整 HTML 程式碼,主要做了兩個調整:
下載時以現時表格狀態為準
在下載函式中,從 Modal 內取出目前呈現的 <table>
(其內容包含所有使用者點選後產生的 inline style 變化),再以此產生完整的 HTML 內容,這樣下載後的檔案就會呈現出當下的單元格背景色狀態。
下載檔名採用使用者輸入的標題
下載時會讀取標題輸入欄位作為檔名(例如「我的行事曆.doc」)。
請將下列程式碼存成 .html 檔後開啟測試:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
.small-text {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 點選後切換背景色
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 每週資料:包含週次、月份(用於合併儲存格)以及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串
let tableHTML = `
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th class="small-text">週次</th>
<th>月份</th>
${weekDays.map(day => `<th class="small-text">${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些列已輸出月份儲存格(用於合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
// 週次欄
tableHTML += `<td class="small-text">${rowsData[i].week}</td>`;
// 月份欄:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生 7 天欄位:若為日(d==0)或六(d==6)則預設淡灰色背景
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell small-text" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell small-text">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容
let modalHTML = `<h2>${title}</h2>` + tableHTML;
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// 於 Modal 內容更新後,綁定所有 day-cell 的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆
// 此函式抓取目前 Modal 內呈現的表格(包含使用者點選後的 inline style)並產生成完整 HTML 文件下載,
// 檔名使用輸入的標題作為檔名。
function downloadCalendar() {
// 取得使用者輸入的標題作為檔名
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 6px; text-align: center; }
.small-text { font-size: 10px; }
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<h1>${title}</h1>
${tableEl.outerHTML}
</body>
</html>
`;
const blob = new Blob([downloadableHTML], { type: "text/html" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
// 檔名使用輸入的標題(例如:我的行事曆.doc)
a.download = title + ".doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
點選狀態捕捉:
使用者在 Modal 內點選單元格後,單元格的 inline style 會即時改變,下載時取用的是 Modal 中目前的 <table>
結構與 inline style(因此即便後續有變更也都會反映在下載的檔案中)。
檔名採用標題:
下載檔案會以輸入的標題作為檔名,例如若輸入「我的行事曆」,則下載檔案為「我的行事曆.doc」。
這樣就能符合你「下載行事曆要包含點選後背景色變化」、「檔名用輸入標題」的需求。
生成的行事曆前面9欄內的文字大小都設為10PX
推理花了 10 秒
以下的範例程式碼中,我們在 CSS 加入了一則選擇器規則,讓表格中前 9 欄(包含週次、月份與 7 個星期)的所有儲存格(th 與 td)文字大小都設為 10px。你可以將以下完整程式碼存成 .html 檔案後,直接用瀏覽器測試。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字都設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 點選後切換背景色
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 每週資料:包含週次、月份(用於合併儲存格)以及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串
let tableHTML = `
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7 個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些列已輸出月份儲存格(用於合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
// 週次欄
tableHTML += `<td>${rowsData[i].week}</td>`;
// 月份欄:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生 7 天欄位;若為日(d==0)或六(d==6)則預設淡灰色背景
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容
let modalHTML = `<h2>${title}</h2>` + tableHTML;
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// Modal 內容更新後,綁定所有 day-cell 的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆
// 此函式會取出 Modal 目前呈現的表格(包含使用者更動過的 inline style),並產生成完整 HTML 檔案下載
// 檔名則使用使用者輸入的標題來命名
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body { font-family: "Microsoft JhengHei", sans-serif; margin:20px; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #666; padding: 6px; text-align: center; }
/* 前 9 欄文字大小 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<h1>${title}</h1>
${tableEl.outerHTML}
</body>
</html>
`;
const blob = new Blob([downloadableHTML], { type: "text/html" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = title + ".doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
前 9 欄文字大小設定:
在 CSS 中加入了以下規則:
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
此規則適用於所有位於前 9 欄的標題與儲存格,確保文字大小皆為 10px。
下載檔案完整性:
下載功能會抓取目前 Modal 內已更新的表格內容(包含使用者點選後的背景色 inline style),並組成完整 HTML 檔案。檔名則使用使用者輸入的標題,如「我的行事曆.doc」。
這份程式碼整合了所有需求,滿足前 9 欄 10px 的文字設定,同時下載的檔案會反映當下表格狀態。
下載的行事曆請完全保留原來html的外觀及文字大小的設定
推理花了 25 秒
下方的範例程式碼中,我們將下載功能更新,讓下載的檔案內包含完整的 CSS 設定(包含背景、表格版型、文字大小等設定),並完整呈現與瀏覽器上所呈現的行事曆外觀一致,同時檔名會以使用者輸入的標題命名。請將下列程式碼存成 .html 檔後開啟試用:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字都設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(請選擇該週的「日」):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 點選後切換背景色(點選時依使用者目前的 inline 狀態做切換)
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
// 這裡以 rgb(240, 240, 240) 為淡灰色判斷
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
const startDate = new Date(startDateStr);
let rowsData = [];
let currentDate = new Date(startDate);
// 每週資料:包含週次、月份(用於合併儲存格)以及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串
let tableHTML = `
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7 個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些列已輸出月份儲存格(用於合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
// 週次欄
tableHTML += `<td>${rowsData[i].week}</td>`;
// 月份欄:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生 7 天欄位:若為日(d==0)或六(d==6)則預設淡灰色背景
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容(完整呈現標題與表格)
let modalHTML = `<h2>${title}</h2>` + tableHTML;
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// Modal 內容更新後,重新綁定所有 day-cell 的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆:將目前 Modal 中呈現的表格,連同完整 CSS 都包入下載的 HTML 文件中
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
// 此下載 HTML 會保留原來的整體外觀及文字大小設定
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字均設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
${tableEl.outerHTML}
</div>
</body>
</html>
`;
const blob = new Blob([downloadableHTML], { type: "text/html" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = title + ".doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
CSS 完整性
在下載功能中,我們將整份 CSS(從 body、.container、h1 到 table 的排版與前 9 欄 10px 的文字大小設定)都嵌入下載的 HTML 頁首,確保下載的行事曆外觀與原先在瀏覽器中看到的一模一樣。
下載檔名採用標題
下載時會讀取使用者輸入的標題,並以此作為下載檔名(例:我的行事曆.doc)。
使用者互動效果保留
由於行事曆中的各日期儲存格若點選後其 inline style 會更新,下載的 HTML 即會反映當下狀態,保留使用者的點選背景色變化。
請試試此版本,下載後你會發現行事曆完全保留了原來 HTML 的外觀及文字大小設定。
在選擇起始日期時,不管選擇那一天,都會以當週的日來生成行事曆
推理花了 6 秒
下面的程式碼中,在產生行事曆時,無論你選擇哪一天作為起始日期,系統都會自動調整計算,找出該週的「日」(即星期天)作為生成行事曆的起點。這裡的關鍵在於利用 JavaScript 的 getDay() 方法取得當天的星期值,若不是 0(表示星期日),則向前調整相應的天數,以取得該週的星期日。請參考以下完整程式碼:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字都設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(不論選哪一天,都會以該週的「日」產生行事曆):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 點選後切換背景色
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
// 取得輸入的日期,並調整成該週的星期日
const inputDate = new Date(startDateStr);
const weekDay = inputDate.getDay(); // 0 表示 Sunday
// 若選擇的日期不是 Sunday,就往前調整到該週的 Sunday
const startDate = new Date(inputDate.getTime());
if (weekDay !== 0) {
startDate.setDate(startDate.getDate() - weekDay);
}
let rowsData = [];
let currentDate = new Date(startDate);
// 每週資料:包含週次、月份(用於合併儲存格)以及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串
let tableHTML = `
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7 個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些列已輸出月份儲存格(用於合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
// 週次欄
tableHTML += `<td>${rowsData[i].week}</td>`;
// 月份欄:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生 7 天欄位:若為日(d==0)或六(d==6)則預設淡灰色背景
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容(完整呈現標題與表格)
let modalHTML = `<h2>${title}</h2>` + tableHTML;
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// Modal 內容更新後,重新綁定所有 day-cell 的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆:將目前 Modal 中呈現的表格與完整 CSS 包入下載文件中
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字均設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
${tableEl.outerHTML}
</div>
</body>
</html>
`;
const blob = new Blob([downloadableHTML], { type: "text/html" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = title + ".doc";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
起始日期調整:
在 generateCalendar() 中,先讀取使用者選取的日期,再利用 getDay() 判斷當前星期,若非 Sunday,則用 setDate() 調整日期,使其變成該週的星期日。
行事曆生成與下載:
生成的行事曆會根據調整後的起始日期產生;下載時則完整保留了原有的 CSS 設定(包括前 9 欄文字大小 10px 等樣式),確保下載後的文件外觀與原本一致。
請將此程式碼存成 .html 檔案後使用瀏覽器開啟試用,就可以看到生成的行事曆總是以該週的「日」為起始日期了。
謝謝
不客氣!😊
很高興能幫上忙,如果之後你還想加入其他功能(像是節日自動標記、顏色標籤、匯出 PDF 等),也都可以再找我~
祝你行事曆製作順利、天天都安排得剛剛好!📅✨
下載行事曆,改為下載docx
推理花了 55 秒
以下這份範例程式碼示範如何將目前行事曆內容包裝成 DOCX 格式檔案下載,其主要作法是利用
JSZip 函式庫,將一個符合 DOCX 檔格式(實際上是 Zip 檔案內含多組 XML 與 HTML 文件)的包裝內容產生出來,並藉由 altChunk 的方式把完整 HTML(包含外觀與文字大小設定)嵌入到 DOCX 中。
在此方案中,產生的 DOCX 包含下列檔案:
[Content_Types].xml
定義了各個部件的 Content-Type。
_rels/.rels
DOCX 的主要關係檔,指定主要文件的位置。
word/document.xml
DOCX 的主要文件,其內利用 <w:altChunk>
元素來引用外部 HTML。
word/_rels/document.xml.rels
定義了 document.xml 與 altChunk 的關係,指定 altChunk 所在的 HTML 路徑。
html/document.html
實際上存放行事曆 HTML 的文件,此文件的內容即為目前呈現的行事曆(包含使用者的點選變化與 CSS 外觀設定)。
注意:使用 altChunk 嵌入 HTML 的方式,Microsoft Word (2013 以上版本) 均可正確讀取。如果您使用舊版 Office,可能會略有差異。
以下完整程式碼中,下載功能已改為使用 JSZip 產生 DOCX 文件(並用使用者輸入的標題作為檔名、副檔名為 .docx),您只需將此檔案儲存為 .html,然後以瀏覽器開啟即可試用。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<!-- 引入 JSZip 函式庫 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字均設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(不論選哪一天,都會以該週的「日」產生行事曆):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal overlay 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<!-- 生成的行事曆內容會放在這裡 -->
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆 (DOCX)</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱(依順序,第一個為「日」)
const weekDays = ['日','一','二','三','四','五','六'];
// 點選後切換背景色
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
// 取得輸入的日期,並調整成該週的星期日
const inputDate = new Date(startDateStr);
const weekDay = inputDate.getDay(); // 0 表示 Sunday
const startDate = new Date(inputDate.getTime());
if (weekDay !== 0) {
startDate.setDate(startDate.getDate() - weekDay);
}
let rowsData = [];
let currentDate = new Date(startDate);
// 每週資料:包含週次、月份(用於合併儲存格)以及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = {
week: i + 1,
month: currentDate.getMonth() + 1,
days: []
};
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串
let tableHTML = `
<table>
<colgroup>
<!-- 左側 9 欄:週次、月份、7 個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 旗標陣列記錄哪些列已輸出月份儲存格(用於合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
// 週次欄
tableHTML += `<td>${rowsData[i].week}</td>`;
// 月份欄:僅第一筆顯示,並計算 rowspan
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
// 產生 7 天欄位;若為日(d==0)或六(d==6)則預設淡灰色背景
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
// 右側兩欄:學校行事與備註
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容(完整呈現標題與表格)
let modalHTML = `<h2>${title}</h2>` + tableHTML;
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// Modal 內容更新後,重新綁定所有 day-cell 的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
// 顯示 Modal
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆為 DOCX
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
// 產生完整 HTML 文件內容(此內容即是我們要嵌入 DOCX 的 HTML)
// ※ 此處的 downloadableHTML 就與之前下載 HTML 時相同
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
${tableEl.outerHTML}
</div>
</body>
</html>
`;
// 以下利用 JSZip 組出一個基本 DOCX 檔案,其中採用 altChunk 將 HTML 嵌入進來。
var zip = new JSZip();
// [Content_Types].xml
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/html/document.html" ContentType="text/html"/>
</Types>`);
// _rels/.rels
zip.folder("_rels").file(".rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`);
// word/document.xml
zip.folder("word").file("document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<w:body>
<w:altChunk r:id="htmlChunk"/>
<w:sectPr>
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440"/>
</w:sectPr>
</w:body>
</w:document>`);
// word/_rels/document.xml.rels
var wordRels = zip.folder("word").folder("_rels");
wordRels.file("document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="htmlChunk"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk"
Target="../html/document.html"/>
</Relationships>`);
// html/document.html
var htmlFolder = zip.folder("html");
htmlFolder.file("document.html", downloadableHTML);
// 產生 zip 檔後下載,設定 MIME 為 DOCX
zip.generateAsync({type:"blob"})
.then(function(content) {
var a = document.createElement("a");
var url = URL.createObjectURL(content);
a.href = url;
a.download = title + ".docx";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
下載 DOCX 功能:
改寫的 downloadCalendar()
函式先從頁面中抓取目前的行事曆表格內容(包含使用者變更的 inline style),並組成完整 HTML(downloadableHTML)。
接著利用 JSZip 建立 DOCX 所需的各個組件,並採用 altChunk 方式(在 word/document.xml
中)將 HTML 嵌入。
最後產生 Zip Blob,指定 MIME 類型下載,檔名以使用者輸入的標題並以 .docx
為副檔名。
外觀保留:
下載的 DOCX 文件內含完整 CSS 與 HTML 內容,故能保留您在瀏覽器上所見的外觀與前 9 欄文字大小設定。
請試試此版本,下載後的檔案即可以 DOCX 格式儲存並由 Word 開啟,呈現與頁面上相同的行事曆外觀。
下載的docx,前9欄的寬度設為0.3公分,文字大小設為10PX,文件邊界設為左右各為1公分
思考 38 秒
以下提供更新後的完整範例程式碼,下載的 DOCX 將滿足以下要求:
DOCX 內文件的左右邊界設為 1 公分(透過修改 document.xml 中的 w:left 與 w:right 屬性,使用約 567 twips 表示 1 公分)。
內部表格中前 9 欄的寬度調整為 0.3 公分(在下載用 HTML 的 style 中加入針對 col 的規則覆寫)。
表格中前 9 欄的文字大小設為 10px(原有的 CSS 規則保留)。
此範例採用 JSZip 與 altChunk 的方式產生 DOCX,請將下列程式碼儲存為 .html 檔後用瀏覽器開啟試用:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<!-- 引入 JSZip 函式庫 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字皆設為 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(不論選哪一天,都會以該週的「日」產生行事曆):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal 用以顯示生成的行事曆與下載按鈕 -->
<div id="modalOverlay">
<div id="modalContent">
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆 (DOCX)</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱:從日到六
const weekDays = ['日','一','二','三','四','五','六'];
// 點選後切換背景色(使用者在行事曆內點選後切換為或清除淡灰背景)
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
// 以使用者選取的日期調整為該週的星期日
const inputDate = new Date(startDateStr);
const weekDay = inputDate.getDay(); // 若非 Sunday (0)
const startDate = new Date(inputDate.getTime());
if (weekDay !== 0) {
startDate.setDate(startDate.getDate() - weekDay);
}
let rowsData = [];
let currentDate = new Date(startDate);
// 生成每週資料:週次、該週第一天的月份(合併儲存格用)及 7 天日期
for (let i = 0; i < weeksCount; i++) {
let weekData = { week: i + 1, month: currentDate.getMonth() + 1, days: [] };
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串(含 colgroup、thead 及 tbody)
let tableHTML = `
<table>
<colgroup>
<!-- 前 9 欄:週次、月份及 7 個星期,下載時第一 9 欄覆寫為 0.3cm -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 右側 2 欄:學校行事與備註,不變 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
// 利用旗標記錄月份儲存格是否已產生(以便合併)
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
tableHTML += `<td>${rowsData[i].week}</td>`;
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 顯示內容
let modalHTML = `<h2>${title}</h2>` + tableHTML;
document.getElementById('calendarDisplay').innerHTML = modalHTML;
// 綁定行事曆內所有日期單元格的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => {
cell.addEventListener('click', toggleCellBackground);
});
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆為 DOCX,並使用使用者輸入的標題作為檔名
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
// 產生完整 HTML,外觀與瀏覽器上相同,但下載時覆寫第一 9 欄寬度為 0.3cm 與文字大小 10px
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
}
table col:nth-child(-n+9) {
width: 0.3cm !important;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
${tableEl.outerHTML}
</div>
</body>
</html>
`;
// 利用 JSZip 組合 DOCX 結構,採用 altChunk 嵌入 HTML 文件內容
var zip = new JSZip();
// [Content_Types].xml
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/html/document.html" ContentType="text/html"/>
</Types>`);
// _rels/.rels
zip.folder("_rels").file(".rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`);
// word/document.xml
// 設定文件邊界左右 1 公分 (約 567 twips)
zip.folder("word").file("document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<w:body>
<w:altChunk r:id="htmlChunk"/>
<w:sectPr>
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="567" w:bottom="1440" w:left="567"/>
</w:sectPr>
</w:body>
</w:document>`);
// word/_rels/document.xml.rels
var wordRels = zip.folder("word").folder("_rels");
wordRels.file("document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="htmlChunk"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk"
Target="../html/document.html"/>
</Relationships>`);
// html/document.html 存放下載用完整 HTML 文件內容
var htmlFolder = zip.folder("html");
htmlFolder.file("document.html", downloadableHTML);
// 利用 JSZip 產生 Blob 並下載,檔名以使用者輸入標題、附檔名為 .docx
zip.generateAsync({type:"blob"})
.then(function(content) {
var a = document.createElement("a");
var url = URL.createObjectURL(content);
a.href = url;
a.download = title + ".docx";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
}
// 關閉 Modal
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
文件邊界調整:
在 word/document.xml 中,將 w:left 與 w:right 的值分別設為 "567"(約 1 公分)。
前 9 欄欄寬調整:
在下載用 HTML 中,透過 CSS 規則
table col:nth-child(-n+9) {
width: 0.3cm !important;
}
強制覆蓋前 9 欄的寬度。
文字大小設定:
已以 CSS 規則確保前 9 欄文字皆為 10px。
此版本下載的 DOCX 檔案,當用 Word 開啟時,會呈現與網頁上相同的行事曆外觀,同時前 9 欄寬度調為 0.3 公分,文字大小 10px,文件左右邊界均為 1 公分。
以下的程式碼範例已修改生成表格的部份,將標題顯示在表格第一列,所有欄位合併(colspan=11),標題文字設為 16px、置中,且第一列的儲存格只顯示下框線。下列範例同時修改了在 Modal 顯示及下載 DOCX(使用 altChunk 嵌入 HTML)的部分,下載的 DOCX 文件也會保留相同外觀,其前 9 欄寬度覆寫為 0.3 公分、文字大小 10px,文件左右邊界各為 1 公分。
請將下列程式碼存成 .html 檔後,以瀏覽器開啟試用:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<!-- 引入 JSZip 函式庫 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格基本樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字設 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
/* 標題列樣式,置中、文字16px,只顯示下框線 */
.table-title td {
font-size: 16px;
text-align: center;
border: none;
border-bottom: 1px solid #666;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(不論選哪一天,都會以該週的「日」產生行事曆):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal 區塊 -->
<div id="modalOverlay">
<div id="modalContent">
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆 (DOCX)</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱:從日到六
const weekDays = ['日','一','二','三','四','五','六'];
// 點選時切換背景色(點選後以 inline style 切換淡灰背景)
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
// 以使用者選取的日期調整為該週的星期日
const inputDate = new Date(startDateStr);
const weekDay = inputDate.getDay();
const startDate = new Date(inputDate.getTime());
if (weekDay !== 0) {
startDate.setDate(startDate.getDate() - weekDay);
}
let rowsData = [];
let currentDate = new Date(startDate);
for (let i = 0; i < weeksCount; i++) {
let weekData = { week: i + 1, month: currentDate.getMonth() + 1, days: [] };
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串,插入表頭的第一列為標題行,合併所有 11 欄
let tableHTML = `
<table>
<colgroup>
<!-- 前 9 欄(週次、月份、7個星期)-->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 後 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr class="table-title">
<td colspan="11">${title}</td>
</tr>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
tableHTML += `<td>${rowsData[i].week}</td>`;
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容
document.getElementById('calendarDisplay').innerHTML = tableHTML;
// 綁定所有 day-cell 點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => cell.addEventListener('click', toggleCellBackground));
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆為 DOCX(保留完整 HTML/CSS 外觀)
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
// 下載的 HTML 內容:內含 container、表格(其中前 9 欄以 CSS 覆寫寬度 0.3cm 及文字 10px)
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 { text-align: center; color: #333; }
table { width: 100%; border-collapse: collapse; }
table col:nth-child(-n+9) { width: 0.3cm !important; }
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
table th:nth-child(-n+9),
table td:nth-child(-n+9) { font-size: 10px; }
.table-title td {
font-size: 16px;
text-align: center;
border: none;
border-bottom: 1px solid #666;
}
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
${tableEl.outerHTML}
</div>
</body>
</html>
`;
// 利用 JSZip 產生 DOCX 結構,以 altChunk 嵌入 HTML
var zip = new JSZip();
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/html/document.html" ContentType="text/html"/>
</Types>`);
zip.folder("_rels").file(".rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
Target="word/document.xml"/>
</Relationships>`);
// word/document.xml 設定文件邊界左右 1 公分 (約 567 twips)
zip.folder("word").file("document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<w:body>
<w:altChunk r:id="htmlChunk"/>
<w:sectPr>
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="567" w:bottom="1440" w:left="567"/>
</w:sectPr>
</w:body>
</w:document>`);
var wordRels = zip.folder("word").folder("_rels");
wordRels.file("document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="htmlChunk"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk"
Target="../html/document.html"/>
</Relationships>`);
var htmlFolder = zip.folder("html");
htmlFolder.file("document.html", downloadableHTML);
zip.generateAsync({type:"blob"}).then(function(content) {
var a = document.createElement("a");
var url = URL.createObjectURL(content);
a.href = url;
a.download = title + ".docx";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
}
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
表格標題列
在表格的 <thead>
區段新增第一個 <tr>
(class 為 table-title
),此列的單一 <td>
設定 colspan 為 11(共 11 欄)並顯示使用者輸入標題。該儲存格僅顯示下邊框,文字大小 16px 且置中。
下載 DOCX 部分
下載的 HTML 文件中,透過 CSS 覆寫前 9 欄欄寬為 0.3 公分、文字大小 10px,並保留原有外觀;DOCX 文件利用 altChunk 方式嵌入該 HTML,並在 Word 中顯示時設定左右邊界各 1 公分。
希望這份程式碼能滿足你的需求,如有其他調整再歡迎告知!
下載的docx,不要有最上方的標題文字了, 只保留表格內的標題文字
推理花了 4 秒
以下程式碼範例將下載 DOCX 時所嵌入的 HTML 部分修改,不再另外加入文件最上方的標題(h1),只保留表格內原有的標題列(已在表格中以 colspan 呈現的標題文字),這樣下載的 DOCX 內容就只顯示表格,不會重複出現標題。請參考下列完整程式碼:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>學期行事曆生成器</title>
<!-- 引入 JSZip 函式庫 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin: 10px 0 5px;
font-weight: bold;
color: #555;
}
input[type="text"],
input[type="date"],
input[type="number"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
margin-top: 15px;
border: none;
border-radius: 4px;
background-color: #4285F4;
color: white;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #3073e5;
}
/* Modal overlay */
#modalOverlay {
display: none;
position: fixed;
z-index: 1000;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.8);
overflow: auto;
padding: 20px;
}
#modalContent {
background: #fff;
margin: 40px auto;
padding: 20px;
border-radius: 8px;
max-width: 90%;
}
/* 表格基本樣式 */
table {
width: 100%;
border-collapse: collapse;
}
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
/* 前 9 欄文字設 10px */
table th:nth-child(-n+9),
table td:nth-child(-n+9) {
font-size: 10px;
}
/* 表格內的標題列樣式:置中、文字16px,只顯示下框線 */
.table-title td {
font-size: 16px;
text-align: center;
border: none;
border-bottom: 1px solid #666;
}
.day-cell {
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>學期行事曆生成器</h1>
<form id="calendarForm">
<label for="title">行事曆標題:</label>
<input type="text" id="title" placeholder="請輸入行事曆標題" required>
<label for="startDate">起始日期(不論選哪一天,都會以該週的「日」產生行事曆):</label>
<input type="date" id="startDate" required>
<label for="weeks">生成週數:</label>
<input type="number" id="weeks" min="1" placeholder="例如:16" required>
<button type="button" onclick="generateCalendar()">生成行事曆</button>
</form>
</div>
<!-- Modal 區塊 -->
<div id="modalOverlay">
<div id="modalContent">
<div id="calendarDisplay"></div>
<div style="text-align: center; margin-top: 15px;">
<button onclick="downloadCalendar()">下載行事曆 (DOCX)</button>
<button style="background-color: #d33;" onclick="closeModal()">關閉</button>
</div>
</div>
</div>
<script>
// 星期名稱:從日到六
const weekDays = ['日','一','二','三','四','五','六'];
// 點選時切換背景色:切換 inline style 淡灰背景
function toggleCellBackground(event) {
const cell = event.target;
const currentBg = window.getComputedStyle(cell).backgroundColor;
if (currentBg === "rgb(240, 240, 240)") {
cell.style.backgroundColor = "";
} else {
cell.style.backgroundColor = "#f0f0f0";
}
}
// 生成行事曆,並在 Modal 中顯示
function generateCalendar() {
const title = document.getElementById('title').value.trim();
const startDateStr = document.getElementById('startDate').value;
const weeksCount = parseInt(document.getElementById('weeks').value, 10);
if (!title || !startDateStr || !weeksCount) {
alert("請完整輸入所有欄位!");
return;
}
// 以使用者選取的日期調整為該週的星期日
const inputDate = new Date(startDateStr);
const weekDay = inputDate.getDay();
const startDate = new Date(inputDate.getTime());
if (weekDay !== 0) {
startDate.setDate(startDate.getDate() - weekDay);
}
let rowsData = [];
let currentDate = new Date(startDate);
for (let i = 0; i < weeksCount; i++) {
let weekData = { week: i + 1, month: currentDate.getMonth() + 1, days: [] };
for (let d = 0; d < 7; d++) {
weekData.days.push(currentDate.getDate());
currentDate.setDate(currentDate.getDate() + 1);
}
rowsData.push(weekData);
}
// 組合表格 HTML 字串,包含表格標題列 (已以 colspan 呈現於表格內)
let tableHTML = `
<table>
<colgroup>
<!-- 前 9 欄:週次、月份及 7 個星期 -->
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<col style="width:3.70%;">
<!-- 後 2 欄:學校行事與備註 -->
<col style="width:50%;">
<col style="width:16.67%;">
</colgroup>
<thead>
<tr class="table-title">
<td colspan="11">${title}</td>
</tr>
<tr>
<th>週次</th>
<th>月份</th>
${weekDays.map(day => `<th>${day}</th>`).join('')}
<th>學校行事</th>
<th>備註</th>
</tr>
</thead>
<tbody>
`;
const skipMonth = Array(rowsData.length).fill(false);
for (let i = 0; i < rowsData.length; i++) {
tableHTML += `<tr>`;
tableHTML += `<td>${rowsData[i].week}</td>`;
if (!skipMonth[i]) {
let rowspan = 1;
for (let j = i + 1; j < rowsData.length; j++) {
if (rowsData[j].month === rowsData[i].month) {
rowspan++;
skipMonth[j] = true;
} else {
break;
}
}
tableHTML += `<td rowspan="${rowspan}">${rowsData[i].month}月</td>`;
}
for (let d = 0; d < 7; d++) {
if (d === 0 || d === 6) {
tableHTML += `<td class="day-cell" style="background-color: #f0f0f0;">${rowsData[i].days[d]}</td>`;
} else {
tableHTML += `<td class="day-cell">${rowsData[i].days[d]}</td>`;
}
}
tableHTML += `<td></td><td></td>`;
tableHTML += `</tr>`;
}
tableHTML += `
</tbody>
</table>
`;
// 更新 Modal 內容
document.getElementById('calendarDisplay').innerHTML = tableHTML;
// 綁定所有日期儲存格的點選事件
const modalCells = document.getElementById('calendarDisplay').querySelectorAll('.day-cell');
modalCells.forEach(cell => cell.addEventListener('click', toggleCellBackground));
document.getElementById('modalOverlay').style.display = "block";
}
// 下載行事曆為 DOCX(下載的 HTML 不再包含文件最上方的 <h1> 標題,只保留表格)
function downloadCalendar() {
const title = document.getElementById('title').value.trim() || "calendar";
const tableEl = document.getElementById('calendarDisplay').querySelector('table');
if (!tableEl) {
alert('找不到表格內容!');
return;
}
// 下載用 HTML:僅包含 container 內部的表格(表格中已包含標題列)
const downloadableHTML = `
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>${title}</title>
<style>
body {
font-family: "Microsoft JhengHei", sans-serif;
background-color: #f7f7f7;
margin: 20px;
padding: 0;
}
.container {
max-width: 1000px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* 表格樣式:前 9 欄覆寫寬度 0.3cm、文字 10px */
table { width: 100%; border-collapse: collapse; }
table col:nth-child(-n+9) { width: 0.3cm !important; }
table th, table td {
border: 1px solid #666;
text-align: center;
padding: 6px;
}
table th:nth-child(-n+9),
table td:nth-child(-n+9) { font-size: 10px; }
.table-title td {
font-size: 16px;
text-align: center;
border: none;
border-bottom: 1px solid #666;
}
.day-cell { cursor: pointer; }
</style>
</head>
<body>
<div class="container">
${tableEl.outerHTML}
</div>
</body>
</html>
`;
// 利用 JSZip 建立 DOCX 結構,內含 altChunk 嵌入上述 HTML
var zip = new JSZip();
zip.file("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/html/document.html" ContentType="text/html"/>
</Types>`);
zip.folder("_rels").file(".rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`);
// 在 document.xml 中設定文件左右邊界為 1 公分 (約 567 twips)
zip.folder("word").file("document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<w:body>
<w:altChunk r:id="htmlChunk"/>
<w:sectPr>
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="567" w:bottom="1440" w:left="567"/>
</w:sectPr>
</w:body>
</w:document>`);
var wordRels = zip.folder("word").folder("_rels");
wordRels.file("document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="htmlChunk"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk"
Target="../html/document.html"/>
</Relationships>`);
var htmlFolder = zip.folder("html");
htmlFolder.file("document.html", downloadableHTML);
zip.generateAsync({type:"blob"}).then(function(content) {
var a = document.createElement("a");
var url = URL.createObjectURL(content);
a.href = url;
a.download = title + ".docx";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
}
function closeModal() {
document.getElementById('modalOverlay').style.display = "none";
}
</script>
</body>
</html>
表格標題列
在表格中已以第一個 <tr>
(class="table-title")顯示使用者輸入的標題,並以 colspan=11 合併整行。下載的 HTML 僅保留此表格,不另外加入最上方的 <h1>
標題。
下載 DOCX 的 HTML
下載的 HTML 部分在 <style>
中增加了規則,將前 9 欄寬度覆寫為 0.3cm 並設定文字為 10px,同時保留其餘外觀設定;文件邊界在 DOCX 的 document.xml 中設定左右均為約 1 公分(567 twips)。
生成 DOCX
利用 JSZip 將 DOCX 內部結構組合完畢,並以 altChunk 嵌入上述 HTML,再以使用者輸入標題作為檔名下載副檔名為 .docx。
以上程式碼即能達到「下載的 DOCX 不包含最上方的標題文字,只保留表格內的標題」的需求。如有任何問題或進一步需求,歡迎再提出!
Powered by ChatGPT Exporter