頁面結構:父頁面 (a.html) → iframe (b.html) → 目標元素。需獲取目標元素相對**瀏覽器視口**左上角的絕對坐標。
getBoundingClientRect() 返回的是**相對 iframe 視口**的坐標iframe.contentDocumentfunction getAbsolutePosition(element) {
let x = 0, y = 0;
let current = element;
// 1. 累加 iframe 內部偏移
while (current) {
x += current.offsetLeft - current.scrollLeft;
y += current.offsetTop - current.scrollTop;
current = current.offsetParent;
}
// 2. 如果在 iframe 中,遞歸處理父文檔
const win = element.ownerDocument.defaultView || element.ownerDocument.parentWindow;
if (win !== win.top) { // 在 iframe 中
const iframe = win.frameElement; // 獲取 iframe 元素
if (iframe) {
const parentPos = getAbsolutePosition(iframe);
x += parentPos.x;
y += parentPos.y;
}
}
return { x, y };
}
// 使用
const target = document.getElementById('targetInIframe');
const pos = getAbsolutePosition(target);
console.log('絕對坐標:', pos.x, pos.y);
function getViewportOffset(element) {
const rect = element.getBoundingClientRect();
let x = rect.left;
let y = rect.top;
const win = element.ownerDocument.defaultView;
if (win !== win.top) {
const iframe = win.frameElement;
const parentOffset = getViewportOffset(iframe);
x += parentOffset.x;
y += parentOffset.y;
}
return { x, y };
}
function getOffsetInParent(element) {
let $el = $(element);
let offset = $el.offset(); // 相對文檔
// 如果在 iframe 中,遞歸加上父文檔偏移
let win = element.ownerDocument.defaultView;
while (win !== win.top) {
let $iframe = $(win.frameElement);
let iframeOffset = $iframe.offset();
offset.left += iframeOffset.left;
offset.top += iframeOffset.top;
win = win.parent;
}
return offset;
}
// 子頁面 (iframe 內)
window.addEventListener('message', function(event) {
if (event.data.type === 'GET_POSITION') {
const el = document.getElementById(event.data.id);
const rect = el.getBoundingClientRect();
event.source.postMessage({
type: 'POSITION_RESULT',
id: event.data.id,
x: rect.left,
y: rect.top
}, event.origin);
}
});
// 父頁面
function getIframeElementPosition(iframe, elementId) {
return new Promise((resolve) => {
const handler = (event) => {
if (event.data.type === 'POSITION_RESULT' && event.data.id === elementId) {
window.removeEventListener('message', handler);
// 還需加上 iframe 自身偏移
const iframeRect = iframe.getBoundingClientRect();
resolve({
x: event.data.x + iframeRect.left,
y: event.data.y + iframeRect.top
});
}
};
window.addEventListener('message', handler);
iframe.contentWindow.postMessage({ type: 'GET_POSITION', id: elementId }, '*');
});
}
// iframe 內鼠標事件 → 父頁面坐標
iframe.contentDocument.addEventListener('mousemove', function(e) {
const iframeRect = iframe.getBoundingClientRect();
const parentX = e.clientX + iframeRect.left;
const parentY = e.clientY + iframeRect.top;
// 在父頁面顯示 tooltip 等
});
| 場景 | 核心 API | 關鍵點 |
|---|---|---|
| 同域 iframe 內元素 → 視口 | getBoundingClientRect() + 遞歸加 frameElement 偏移 | 遞歸處理多層嵌套 |
| 跨域 iframe 內元素 → 視口 | postMessage 通信 | 雙方協議約定、驗證 origin |
| iframe 內鼠標事件 → 父頁面 | e.clientX/Y + iframe.getBoundingClientRect() | clientX/Y 已是相對視口 |