【问题标题】:getBoundingClientRect from within iFrame来自 iFrame 的 getBoundingClientRect
【发布时间】:2018-10-30 03:05:27
【问题描述】:

我有一个函数来评估元素(iFrame)是否在视口内,如果元素在视图中,则返回 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 的视口而不是主窗口?

有什么方法可以在带有getBoundingClientRect() 的 iFrame 中使用主窗口视口

【问题讨论】:

    标签: javascript iframe cross-domain getboundingclientrect


    【解决方案1】:

    每个 iframe 都有自己的范围,因此 iframe 内的窗口与 root 窗口不同。

    您可以通过window.top 获取根窗口,并根据这些知识计算当前 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;
    

    这应该可行。

    【讨论】:

    • ofc,这不包括安全框架,因为您无权访问父窗口
    • 是的,这是真的。并且没有其他可能做到这一点。
    【解决方案2】:

    这是跨域 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);
      });
    }
    

    【讨论】:

    • GET_IFRAME_COORDINATES_MESSAGE 是什么?
    • @Alex,它只是一个任意常量字符串(频道名称)
    猜你喜欢
    • 1970-01-01
    • 2014-04-07
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    相关资源
    最近更新 更多