<frameset cols="280,*" frameborder="yes" bordercolor="silver">
<frame src="menuAction!showTree" name="menuTree" id="leftTree" scrolling="no">
<frame src="content.jsp" name="main" id="showContent" scrolling="auto">
</frameset>
右側框架提交表單 (Struts2 Action) 後,需重新載入左側選單反映最新權限/結構。直接返回主頁面會導致巢狀 Frameset。
// 右側頁面 content.jsp - body onload
function init() {
// 方式 1:直接 reload (最簡單)
window.parent.frames["leftTree"].location.reload();
// 方式 2:指定新 URL (可帶參數)
// window.parent.frames["leftTree"].location.href = "menuAction!showTree?timestamp=" + Date.now();
}
<body onload="init()">
<action name="saveContent" class="com.action.ContentAction" method="save">
<result name="success">/content.jsp</result>
<!-- 返回右側頁面,由頁面 onload 觸發左側刷新 -->
</action>
不重載整個左側頁面,僅更新選單中某個 <div id="node-123"> 區塊。
// 右側頁面提交成功後呼叫
function refreshLeftTreeNode(nodeId) {
var xhr = createXHR();
var url = "menuAction!getNodeHtml?nodeId=" + nodeId; // 返回 HTML 片段
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 關鍵:跨框架存取 DOM 並更新
var leftDoc = window.parent.frames["leftTree"].document;
var target = leftDoc.getElementById(nodeId);
if (target) {
target.innerHTML = xhr.responseText;
} else {
console.warn("左側框架找不到元素: " + nodeId);
}
} else {
alert("刷新失敗: " + xhr.status);
}
}
};
xhr.send(null);
}
// 建立跨瀏覽器 XHR
function createXHR() {
if (window.XMLHttpRequest) return new XMLHttpRequest();
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {}
throw new Error("不支援 AJAX");
}
public String getNodeHtml() {
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print(menuService.buildNodeHtml(nodeId)); // 返回 <li>...</li> 片段
return null; // 不走 result
}
| 操作 | 語法 | 說明 |
|---|---|---|
| 取得父視窗 | window.parent | Frameset 頁面的 window 物件 |
| 取得同級框架 | window.parent.frames["frameName"] 或 window.parent.frames[0] | 由 name 或 index 存取 |
| 取得框架 document | window.parent.frames["name"].document | 操作目標框架 DOM |
| 重載框架 | frames["name"].location.reload() | 保留 POST 資料會提示確認 |
| 導向新 URL | frames["name"].location.href = url | 乾淨導向、可帶查詢參數 |
| 呼叫框架函式 | frames["name"].functionName() | 跨框架函式調用 |
若 Frameset 與子框架域名、協定、埠口不一致,window.parent.frames["x"].document 會拋出 SecurityError。
document.domain = "example.com":僅限主域相同、子域不同 (如 a.example.com / b.example.com)| 方案 | 優點 | 遷移建議 |
|---|---|---|
| iframe + postMessage | 保留框架隔離、解決跨域、安全可控 | 逐步替換 frame 為 iframe |
| 單頁應用 (Vue/React + Router) | 無框架問題、體驗流暢、SEO 需 SSR | 重寫前端、API 化後端 |
| AJAX 區塊更新 (jQuery/Fetch) | 輕量、無框架、漸進增強 | 針對局部刷新場景直接改造 |
Frameset 跨框架操作核心在於 window.parent.frames["name"] 存取目標框架 window/document/location 物件。整體刷新用 location.reload() 或 location.href,局部刷新用 AJAX 取得片段後注入 innerHTML。實務上務必處理同源策略、元素不存在、載入失敗等異常情況。新專案強烈建議採用現代前端架構替代 Frameset,存量系統可用本文方案維護延展。