在傳統 `frameset`/`iframe` 架構中,父頁面按鈕點擊觸發子頁面 `table` 動態增刪行是常見需求。本文提供基於原生 JS 的跨文檔 DOM 操作完整方案,涵蓋跨域限制、生命週期同步、性能優化關鍵點。
`iframe` 元素的 `contentWindow` 屬性返回**子頁面的 `window` 物件**,透過它可訪問子頁面的 `document`、全域變數、函式。**前提:同源策略 (協議/域名/端口一致) 或配置 `document.domain` 放寬子域限制。**
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>父頁面</title>
</head>
<body>
<iframe id="childFrame" src="child.html" width="100%" height="400"></iframe>
<br>
<button onclick="addRowToChild()">向子頁面添加行</button>
<button onclick="clearChildTable()">清空子頁面表格</button>
<script>
function getChildDoc() {
const iframe = document.getElementById('childFrame');
// 關鍵:等待 iframe 加載完成
if (!iframe.contentWindow || !iframe.contentWindow.document) {
alert('子頁面尚未加載完成');
return null;
}
return iframe.contentWindow.document;
}
function addRowToChild() {
const doc = getChildDoc();
if (!doc) return;
const table = doc.getElementById('dataTable');
if (!table) { alert('子頁面未找到 dataTable'); return; }
const row = table.insertRow(-1); // -1 追加到末尾
const now = Date.now();
// 插入單元格並設置內容
row.insertCell(0).textContent = 'ID-' + now;
row.insertCell(1).textContent = '動態數據-' + now;
row.insertCell(2).textContent = new Date().toLocaleTimeString();
}
function clearChildTable() {
const doc = getChildDoc();
if (!doc) return;
const table = doc.getElementById('dataTable');
if (table) {
// 保留表頭 (rows[0]),刪除數據行
while (table.rows.length > 1) table.deleteRow(1);
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>子頁面</title>
</head>
<body>
<h3>子頁面數據表格</h3>
<table id="dataTable" border="1" style="border-collapse:collapse; width:100%;">
<thead>
<tr style="background:#f0f0f0;">
<th>ID</th><th>內容</th><th>時間</th>
</tr>
</thead>
<tbody></tbody>
</table>
</body>
</html>
| 點 | 說明 | 最佳實踐 |
|---|---|---|
| 獲取子文檔 | `iframe.contentWindow.document` | 封裝 `getChildDoc()` 統一做空檢查、加載狀態判斷 |
| 插入行 | `table.insertRow(index)` | 索引 `-1` 追加末尾;`0` 插入表頭後;返回 `HTMLTableRowElement` |
| 插入儲存格 | `row.insertCell(index)` | 返回 `HTMLTableCellElement`,用 `textContent` 設置文本 (防 XSS) |
| 刪除行 | `table.deleteRow(index)` | 反向循環刪除避免索引偏移:`for(let i=rows.length-1; i>0; i--) table.deleteRow(i)` |
| 性能優化 | 大量行操作 | 構建 `DocumentFragment` 或字串 `innerHTML` 批量寫入,避免頻繁重排 |
| 跨域處理 | 不同域/端口 | **不可直接操作 DOM**。需用 `postMessage` 通信,子頁面監聽 `message` 事件自行操作 DOM |
const iframe = document.getElementById('childFrame');
iframe.contentWindow.postMessage({
type: 'ADD_ROW',
payload: { id: 'ID-' + Date.now(), content: '跨域數據' }
}, 'https://child-domain.com'); // 指定目標源,安全
window.addEventListener('message', (event) => {
// 安全驗證來源
if (event.origin !== 'https://parent-domain.com') return;
if (event.data.type === 'ADD_ROW') {
const { id, content } = event.data.payload;
const row = document.getElementById('dataTable').insertRow(-1);
row.insertCell(0).textContent = id;
row.insertCell(1).textContent = content;
row.insertCell(2).textContent = new Date().toLocaleTimeString();
}
});
**同源用 `contentWindow.document` 直操作,跨域用 `postMessage` 解耦通信。** 封裝獲取子文檔邏輯、統一異常處理、注意大量操作性能優化,即可構建穩健的跨 Frame 交互系統。現代項目建議優先考慮 **單頁應用 (SPA) + 組件化** 替代 Frame 架構,從根本上消除跨文檔協作複雜度。