` 完整匯出為 Excel,是報表系統常見需求。本文提供成熟的 `TableToExcel.js` 元件,透過 ActiveXObject 自動化 Excel,精確還原合併儲存格、樣式、欄寬、自動換行等細節。
架構設計
- TableToExcel 類別:封裝匯出邏輯,支援鏈式設定字體、邊框、顏色、對齊、自適應欄寬等選項。
- saveAsExcel(tableID):便捷入口函式,一鍵匯出。
- copyhtmltoexcel.js (備選):利用 `execCommand("Copy")` + `Worksheet.Paste()` 簡易方案,適合不需精確合併儲存格的場景。
核心程式碼:TableToExcel.js (完整版)
/**
* HTML Table → Excel 自動化匯出元件
* 相容 IE / Edge (IE 模式) ,需啟用 ActiveX
* @param {string} tableID - 目標 table 元素 id
*/
function TableToExcel(tableID) {
this.tableID = tableID;
// 預設樣式
this.tableBorder = 2; // 邊框粗細 (1-4)
this.backGround = 0; // 背景色索引 (Excel 調色盤)
this.fontColor = 1; // 字體色索引
this.fontSize = 10; // 字體大小
this.fontStyle = "微軟正黑體"; // 字體名稱
this.rowHeight = -1; // 行高 (-1 自動)
this.columnWidth = 7; // 預設欄寬
this.lineWrap = true; // 自動換行
this.textAlign = -4108; // 對齊:-4108=置中, -4131=左, -4152=右
this.autoFit = true; // 自動調整欄寬
}
// 設定方法 (支援鏈式呼叫)
TableToExcel.prototype.setTableBorder = function(v) { this.tableBorder = v; return this; };
TableToExcel.prototype.setBackGround = function(v) { this.backGround = v; return this; };
TableToExcel.prototype.setFontColor = function(v) { this.fontColor = v; return this; };
TableToExcel.prototype.setFontSize = function(v) { this.fontSize = v; return this; };
TableToExcel.prototype.setFontStyle = function(v) { this.fontStyle = v; return this; };
TableToExcel.prototype.setRowHeight = function(v) { this.rowHeight = v; return this; };
TableToExcel.prototype.setColumnWidth = function(v) { this.columnWidth = v; return this; };
TableToExcel.prototype.isLineWrap = function(v) { this.lineWrap = v; return this; };
TableToExcel.prototype.setTextAlign = function(v) { this.textAlign = v; return this; };
TableToExcel.prototype.isAutoFit = function(v) { this.autoFit = v; return this; };
/** 核心匯出邏輯 */
TableToExcel.prototype.getExcelFile = function() {
var excel, workbook, sheet, htmlTable, totalRow, totalCol, cellMap;
try {
excel = new ActiveXObject("Excel.Application");
} catch (e) {
alert("無法啟動 Excel!請確認已安裝 Excel 並調整 IE 安全性設定:\n" +
"工具 → 網際網路選項 → 安全性 → 自訂層級 → ActiveX 控制項與增益集 → " +
"「對未標記為安全的 ActiveX 控制項初始化並指令碼執行」選「啟用」");
return false;
}
excel.Visible = true;
workbook = excel.Workbooks.Add();
excel.DisplayAlerts = false;
// 刪除多餘工作表 (預設 3 張)
workbook.Worksheets(3).Delete();
workbook.Worksheets(2).Delete();
excel.DisplayAlerts = true;
sheet = workbook.ActiveSheet;
// 1. 掃描目標 table,計算實際行數、列數 (含合併)
htmlTable = document.getElementById(this.tableID);
if (!htmlTable) { alert("找不到 table: " + this.tableID); return; }
totalRow = htmlTable.rows.length;
totalCol = 0;
for (var c = 0; c < htmlTable.rows[0].cells.length; c++) {
totalCol += htmlTable.rows[0].cells[c].colSpan || 1;
}
// 2. 建立二維陣列標記儲存格佔用狀態 (避開合併區域)
cellMap = [];
for (var r = 0; r <= totalRow; r++) {
cellMap[r] = [];
for (var c = 0; c <= totalCol; c++) cellMap[r][c] = false;
}
// 3. 設定全域預設欄寬/行高
if (this.columnWidth != -1) sheet.Columns.ColumnWidth = this.columnWidth;
if (this.rowHeight != -1) sheet.Rows.RowHeight = this.rowHeight;
// 4. 逐儲存格轉換
for (var r = 0; r < htmlTable.rows.length; r++) {
var excelRow = r + 1; // Excel 1-based
var excelCol = 1;
var htmlRow = htmlTable.rows[r];
for (var c = 0; c < htmlRow.cells.length; c++) {
var htmlCell = htmlRow.cells[c];
var colSpan = htmlCell.colSpan || 1;
var rowSpan = htmlCell.rowSpan || 1;
// 尋找下一個可用欄位
while (excelCol <= totalCol && cellMap[excelRow][excelCol]) excelCol++;
if (excelCol > totalCol) break;
var startCell = sheet.Cells(excelRow, excelCol);
var endCell = sheet.Cells(excelRow + rowSpan - 1, excelCol + colSpan - 1);
var range = sheet.Range(startCell, endCell);
// 合併儲存格 (若跨列/跨行)
if (rowSpan > 1 || colSpan > 1) {
range.Merge();
}
// 樣式套用
range.HorizontalAlignment = this.textAlign;
range.Font.Size = this.fontSize;
range.Font.Name = this.fontStyle;
range.WrapText = this.lineWrap;
range.Interior.ColorIndex = this.backGround;
range.Font.ColorIndex = this.fontColor;
if (this.tableBorder != -1) {
var borders = [1, 2, 3, 4]; // xlEdgeTop, xlEdgeBottom, xlEdgeLeft, xlEdgeRight
for (var b = 0; b < borders.length; b++) {
range.Borders(borders[b]).Weight = this.tableBorder;
}
}
// 寫入內容 (innerText 純文字,避免 HTML 標籤污染)
range.Value = htmlCell.innerText;
// 標記佔用
for (var rr = excelRow; rr < excelRow + rowSpan; rr++) {
for (var cc = excelCol; cc < excelCol + colSpan; cc++) {
cellMap[rr][cc] = true;
}
}
excelCol += colSpan;
}
}
// 5. 自動調整欄寬
if (this.autoFit) sheet.Columns.AutoFit();
// 6. 釋放 COM 物件
excel.UserControl = true; // 讓使用者控制 Excel 生命週期
excel = null; workbook = null; sheet = null;
};
便捷入口函式
function saveAsExcel(tableID) {
var tb = new TableToExcel(tableID);
tb.setFontStyle("微軟正黑體")
.setFontSize(10)
.setTableBorder(2)
.setColumnWidth(7)
.isLineWrap(false)
.isAutoFit(true)
.getExcelFile();
}
JSP 頁面整合範例
<%@ page contentType="text/html;charset=UTF-8" %>
<html><head>
<script src="TableToExcel.js"></script>
</head><body>
<h3>多層表頭報表示例</h3>
<table id="reportTable" border="1" cellspacing="0" cellpadding="4">
<tr style="background:#4472C4; color:white;">
<th rowspan="2">年度</th>
<th colspan="4">季度營收 (萬元)</th>
<th rowspan="2">年增率</th>
</tr>
<tr style="background:#5B9BD5; color:white;">
<th>Q1</th><th>Q2</th><th>Q3</th><th>Q4</th>
</tr>
<tr><td>2023</td><td>1200</td><td>1350</td><td>1400</td><td>1550</td><td>12.5%</td></tr>
<tr><td>2024</td><td>1300</td><td>1420</td><td>1480</td><td>1620</td><td>8.3%</td></tr>
</table>
<br/>
<input type="button" value="匯出 Excel" onclick="saveAsExcel('reportTable')" />
</body></html>
備選方案:copyhtmltoexcel.js (簡易版)
原理:`document.body.createTextRange().moveToElementText(el).execCommand("Copy")` → `sheet.Paste()`。
優點:程式碼極簡、自動保留 HTML 表格結構。
缺點:無法精確控制合併儲存格邊框、樣式;大量資料效能差;同樣需 ActiveX。
部署與安全性設定 (必要步驟)
- 用戶端必須安裝 Excel** (不可只裝 Viewer)。
- IE 設定:
「工具 → 網際網路選項 → 安全性 → 信任的網站/區域 → 自訂層級」
→ 「ActiveX 控制項與增益集」
→ 「對未標記為安全的 ActiveX 控制項初始化並指令碼執行」 → **啟用**
→ 「執行 ActiveX 控制項」 → **啟用**
- 將網站加入「信任的網站」區域,降低安全性提示。
- 現代瀏覽器 (Edge/Chrome/Firefox) 不支援 ActiveX**
現代化替代方案 (無 ActiveX、跨瀏覽器)
| 方案 | 原理 | 優點 | 缺點 |
| SheetJS (xlsx.js) | 純 JS 解析 Table → 生成 .xlsx 二進位 Blob → `saveAs()` 下載 | 無外掛、跨平台、支援樣式/合併儲存格、效能極佳 | 需引入庫、大檔案記憶體壓力 |
| ExcelJS | 功能更豐富的 JS Excel 操作庫 | 支援串流寫入、公式、圖表、樣式完整 | 體積較大 |
| 後端生成 (Apache POI / NPOI / ClosedXML) | Server 端生成 .xlsx → 提供下載連結 | 無瀏覽器限制、邏輯集中、安全性高、支援超大檔 | 需往返 Server、佔用後端資源 |
SheetJS 快速上手 (前端純 JS)
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<script>
function exportTableToExcel(tableId, filename) {
var table = document.getElementById(tableId);
var wb = XLSX.utils.table_to_book(table, { sheet: "Report", raw: true });
// 可進一步調整欄寬、樣式...
XLSX.writeFile(wb, filename || "Report.xlsx");
}
</script>
<button onclick="exportTableToExcel('reportTable', '年度營收報表.xlsx')">匯出 (SheetJS)</button>
`TableToExcel.js` 為傳統 IE+ActiveX 環境提供**像素級精確控制**的 Table→Excel 匯出方案,完美處理多層表頭、合併儲存格、樣式。現代專案**強烈建議改用 SheetJS/xlsx.js** 或**後端生成**,擺脫 ActiveX 依賴,實現跨瀏覽器、跨平台、無安全性設定困擾的匯出體驗。
- 上一篇文章: JScript 正則表達式完整教學:RegExp 物件、String 方法、進階替換技巧與實戰範例
- 下一篇文章: JSP 分頁實作完全指南:JSTL + EL 表達式打造乾淨分頁組件
Copyright ©
程式碼之家 All Rights Reserved