从 iFrame 中获取 BoundingClientRect
getBoundingClientRect from within iFrame
我有一个函数可以评估元素(iFrame)是否在视口内(如果元素在视图中)returns true。
function isElementInViewport() {
var el = document.getElementById('postbid_if')
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
console.log("eleTom " + elemTop)
console.log("elemBottom " + elemBottom)
console.log("window.innerHeight " + (window.innerHeight + (window.top.innerHeight * 0.5)))
var isVisible = (elemTop >= 0) && (elemBottom <= (window.innerHeight + window.innerHeight * 0.5));
return isVisible;
}
此函数直接在页面上提供时可以正常工作,但在实时环境中运行此函数时它位于 iFrame 内,看起来 getBoundingClientRect()
引用的是 iFrame 的视口而不是主 window?
有什么方法可以在带有 getBoundingClientRect()
的 iFrame 中使用主 window 视口
每个 iframe 都有自己的作用域,因此 iframe 中的 window 不同于 root window.
您可以通过 window.top
获得根目录 window 并且利用这些知识您可以计算当前 iframe 的绝对位置。这是一个正确的函数:
function currentFrameAbsolutePosition() {
let currentWindow = window;
let currentParentWindow;
let positions = [];
let rect;
while (currentWindow !== window.top) {
currentParentWindow = currentWindow.parent;
for (let idx = 0; idx < currentParentWindow.frames.length; idx++)
if (currentParentWindow.frames[idx] === currentWindow) {
for (let frameElement of currentParentWindow.document.getElementsByTagName('iframe')) {
if (frameElement.contentWindow === currentWindow) {
rect = frameElement.getBoundingClientRect();
positions.push({x: rect.x, y: rect.y});
}
}
currentWindow = currentParentWindow;
break;
}
}
return positions.reduce((accumulator, currentValue) => {
return {
x: accumulator.x + currentValue.x,
y: accumulator.y + currentValue.y
};
}, { x: 0, y: 0 });
}
现在在 isElementInViewport
中更改这些行:
var elemTop = rect.top;
var elemBottom = rect.bottom;
至
var currentFramePosition = getCurrentFrameAbsolutePosition();
var elemTop = rect.top + currentFramePosition.y;
var elemBottom = rect.bottom + currentFramePosition.y;
这应该有效。
下面是跨域 iframe 的代码:
window.addEventListener('message', async (event) => {
if (event.data.channel !== GET_IFRAME_COORDINATES_MESSAGE) return;
const coordinates = window === window.top
? { x: 0, y: 0 }
: await getFrameCoordinates();
const iframes = document.getElementsByTagName('iframe');
for (const iframe of iframes) {
if (iframe.src !== event.data.href) continue;
const { x, y } = iframe.getBoundingClientRect();
event.source.postMessage({
channel: `${GET_IFRAME_COORDINATES_MESSAGE}-reply`,
coordinates: {
x: coordinates.x + x,
y: coordinates.y + y,
}
}, '*');
break;
}
});
async function getFrameCoordinates() {
return new Promise((resolve, reject) => {
const listenForFrameCoordinatesResponse = (event) => {
if (event.data.channel !== `${GET_IFRAME_COORDINATES_MESSAGE}-reply`) return;
window.removeEventListener('message', listenForFrameCoordinatesResponse);
resolve(event.data.coordinates);
};
window.addEventListener('message', listenForFrameCoordinatesResponse);
window.parent.postMessage({
channel: GET_IFRAME_COORDINATES_MESSAGE,
href: window.location.href,
}, '*');
setTimeout(() => reject(new Error('Getting frame coordinates timeout')), 2000);
});
}
我有一个函数可以评估元素(iFrame)是否在视口内(如果元素在视图中)returns true。
function isElementInViewport() {
var el = document.getElementById('postbid_if')
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
console.log("eleTom " + elemTop)
console.log("elemBottom " + elemBottom)
console.log("window.innerHeight " + (window.innerHeight + (window.top.innerHeight * 0.5)))
var isVisible = (elemTop >= 0) && (elemBottom <= (window.innerHeight + window.innerHeight * 0.5));
return isVisible;
}
此函数直接在页面上提供时可以正常工作,但在实时环境中运行此函数时它位于 iFrame 内,看起来 getBoundingClientRect()
引用的是 iFrame 的视口而不是主 window?
有什么方法可以在带有 getBoundingClientRect()
每个 iframe 都有自己的作用域,因此 iframe 中的 window 不同于 root window.
您可以通过 window.top
获得根目录 window 并且利用这些知识您可以计算当前 iframe 的绝对位置。这是一个正确的函数:
function currentFrameAbsolutePosition() {
let currentWindow = window;
let currentParentWindow;
let positions = [];
let rect;
while (currentWindow !== window.top) {
currentParentWindow = currentWindow.parent;
for (let idx = 0; idx < currentParentWindow.frames.length; idx++)
if (currentParentWindow.frames[idx] === currentWindow) {
for (let frameElement of currentParentWindow.document.getElementsByTagName('iframe')) {
if (frameElement.contentWindow === currentWindow) {
rect = frameElement.getBoundingClientRect();
positions.push({x: rect.x, y: rect.y});
}
}
currentWindow = currentParentWindow;
break;
}
}
return positions.reduce((accumulator, currentValue) => {
return {
x: accumulator.x + currentValue.x,
y: accumulator.y + currentValue.y
};
}, { x: 0, y: 0 });
}
现在在 isElementInViewport
中更改这些行:
var elemTop = rect.top;
var elemBottom = rect.bottom;
至
var currentFramePosition = getCurrentFrameAbsolutePosition();
var elemTop = rect.top + currentFramePosition.y;
var elemBottom = rect.bottom + currentFramePosition.y;
这应该有效。
下面是跨域 iframe 的代码:
window.addEventListener('message', async (event) => {
if (event.data.channel !== GET_IFRAME_COORDINATES_MESSAGE) return;
const coordinates = window === window.top
? { x: 0, y: 0 }
: await getFrameCoordinates();
const iframes = document.getElementsByTagName('iframe');
for (const iframe of iframes) {
if (iframe.src !== event.data.href) continue;
const { x, y } = iframe.getBoundingClientRect();
event.source.postMessage({
channel: `${GET_IFRAME_COORDINATES_MESSAGE}-reply`,
coordinates: {
x: coordinates.x + x,
y: coordinates.y + y,
}
}, '*');
break;
}
});
async function getFrameCoordinates() {
return new Promise((resolve, reject) => {
const listenForFrameCoordinatesResponse = (event) => {
if (event.data.channel !== `${GET_IFRAME_COORDINATES_MESSAGE}-reply`) return;
window.removeEventListener('message', listenForFrameCoordinatesResponse);
resolve(event.data.coordinates);
};
window.addEventListener('message', listenForFrameCoordinatesResponse);
window.parent.postMessage({
channel: GET_IFRAME_COORDINATES_MESSAGE,
href: window.location.href,
}, '*');
setTimeout(() => reject(new Error('Getting frame coordinates timeout')), 2000);
});
}