Search code examples
javascripthtmldomiframedom-traversal

Walk up DOM through multiple iframes in vanilla JS


I'd like to be able to walk up the DOM from inside one or more nested iframes to grab and empty the parent div of the top iframe. I'd like my JS to work if there's just one iframe, or if there are two.

I have a main page with this iframe:

<div class="ad-unit">
  <div class="ad-unit-large">
    <iframe width="970" height="250" src="ad.html"></iframe>
  </div>
</div>

and then in ad.html, the page inside the iframe, I have this JavaScript:

function getAdSlot() {
  var el = window.frameElement.parentNode;
  for (; el; el = el.parentNode) {
    if (el.classList && el.classList.contains('ad-unit')) return el;
  }
  return false;
}

var el = getAdSlot();
console.log(el);
el.innerHTML = '';

This walks up the DOM of the containing page till it finds the div with class ad-unit and empties it.

This works nicely. But unfortunately sometimes ad.html will be served inside a second iframe nested inside the first, so:

<div class="ad-unit">
  <div class="ad-unit-large">
    <iframe width="970" height="250" src="frame.html"></iframe>
  </div>
</div>

And frame.html will contain:

<iframe width="970" height="250" src="ad.html"></iframe>

Can anyone tell me how I can deal with this case? I've tried starting my script with

var el = document;

But the DOM walking stops when it gets to the first iframe. How can I continue walking up the DOM irrespective of whether the parent node is a div or an iframe?


Solution

  • If you don't find the correct div, you have to go up to the next iframe (or top level window) using parent

    function getAdSlot(win) {
      var el = win.frameElement.parentNode;
      for (; el; el = el.parentNode) {
        if (el.classList && el.classList.contains('ad-unit')) return el;
      }
      // Reached the top, didn't find it 
      if (parent === top) {
         return false;
      }
      // Try again with the parent window 
      return getAdSlot(parent);
    }
    var el = getAdSlot();
    console.log(el);
    el.innerHTML = '';