Breaking out of iframe

May 13th 2022 HTML 5

Iframes are much less common today than they used to be. But there are still use cases where they make sense, such as when embedding a payment provider's form in a web application. This allows you to maintain a common header and footer even when the user interacts with the payment provider's page.

Because navigation within the iframe is independent of navigation in the parent window, it is important to use navigation in the iframe only during its flow and resume navigation in the parent window once that flow is complete. Otherwise, duplicate headers will occur because the next application page is embedded in the iframe just like the payment provider page.

Nested page in iframe

The best way to solve this problem is to request a top-level navigation/redirect from the page within the iframe. If you do not have control over this, your only option is to handle the iframe's load event from the parent page:

<iframe #iframe (load)="onLoaded()" src="...">Loading…</iframe>

If you detect that the iframe has navigated back to the parent application, you can redirect to the correct page from there:

@ViewChild('iframe') iframeElement?: ElementRef<HTMLIFrameElement>;

public onLoaded(): void {
  try {
    const frameLocation =
      this.iframeElement?.nativeElement?.contentWindow?.location?.href;

    if (frameLocation) {
      this.router.navigate(['/thank-you']);
    }
  } catch {
    // catch cross-origin error when trying to read iframe location
  }
}

The try-catch block is important. For security reasons, browsers prohibit access to the iframe URL unless it has the same origin as the parent window. However, that does not prevent you from determining when the iframe was redirected back to your application. When that happens, the origin is the same and you can read the URL. In the code above, I perform the redirect as soon as this happens, but you can of course perform further checks on the URL if needed.

It's also worth noting that the event is not fired until the page in the iframe is fully loaded and already rendered. This means that the user will see the page in the iframe before you can redirect. If this is an issue, you can navigate to a special blank page within the iframe that you use only for detection, and respond by redirecting to the correct page from the parent page.

Although the above code is written in Angular, there is nothing Angular-specific about it. You could implement the same logic in any front-end framework or even vanilla JavaScript. Just use whatever your application is written in.

You can find a small example using this approach in my GitHub repository. I load a Google form into the iframe where the user has to click a link to navigate back to the parent application. But that's only because Google Forms does not support redirection after submission.

For security reasons, the browser disallows most interactions between the parent page and the child page in the iframe unless they have the same origin. In this post, I have described a little trick you can still use if you need to break out of an iframe but do not have control over the page running in the iframe.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License