Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getBoundingClientRect from within iFrame

I have a function to assess whether an element (an iFrame) is within the viewport if the element is in view it 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;
}

This function works correctly when served directly on the page, but in the live environment when this function runs it's inside an iFrame and it looks like getBoundingClientRect() is referencing the viewport of the iFrame rather than the main window?

Is there any way to use the main window viewport from within an iFrame with getBoundingClientRect()

like image 374
Jonathan Devereux Avatar asked Oct 30 '18 03:10

Jonathan Devereux


People also ask

What is getBoundingClientRect () in Javascript?

The getBoundingClientRect() method returns the size of an element and its position relative to the viewport. The getBoundingClientRect() method returns a DOMRect object with eight properties: left, top, right, bottom, x, y, width, height.

Does getBoundingClientRect include border?

Border, padding and margin are not included.

What is getBoundingClientRect relative to?

getBoundingClientRect() gives a result relative to the viewport's top-left corner ( 0,0 ), not relative to an element's parent, whereas el.


1 Answers

Each iframe has his own scope so window inside iframe is different than the root window.

You can get root window by window.top and with that knowledge you could calculate absolute position of current iframe. Here is a proper function:

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 });
}

Now inside isElementInViewport change these lines:

var elemTop = rect.top;
var elemBottom = rect.bottom;

to

var currentFramePosition = getCurrentFrameAbsolutePosition();
var elemTop = rect.top + currentFramePosition.y;
var elemBottom = rect.bottom + currentFramePosition.y;

and this should works.

like image 155
pawelbylina Avatar answered Oct 05 '22 20:10

pawelbylina