iFrame Implementation in a Web Page

Kevin Kurniawan
Blibli.com Tech Blog
4 min readJan 3, 2023

iFrame Introduction

An iFrame is an inline frame that used to embed document and interactive media in a web page. We can use iFrame to display a web page within a web page. The function of iFrame is to add or include content from other sources to our web page. iFrame will integrate content to any area on the page seamlessly.

iFrame Browser Supports

All modern browser support iFrame element.

Reference: https://caniuse.com/?search=iframe (December 23rd, 2022)

How to Implement iFrame

The implementation that I will share here is based on my experience when implementing using Vue.js v2 framework.

Using iFrame HTML element

<iframe
v-if="childUrl"
:src="childUrl"
:height="iframeHeight"
:scrolling="iframeScrollable"
width="100%"
frameborder="0"
/>

<iframe> HTML element has several attributes to adjust the embedded content appearance based on what we expect.

  • src: the URL of child page (web page to embed).
  • width: the width size of the iFrame element.
  • height: the heigth size of the iFrame element.
  • scrolling: to specify whether the iFrame element is scrollable or not (true/false).
  • frameborder: to specify whether display the border around the content or not (true/false).
  • name: to specify a name for an iframe. This can be used as reference (selector) in JavaScript.
  • title: to describe the contents of the frame.
  • sandbox: enables an extra set of restrictions for the content in the iframe.

Using vue-friendly-iframe

<template>
<div class="detail">
<vue-friendly-iframe
:src="childUrl"
className="custom-iframe"
frameborder="0"
/>
</div>
</template>

<script src="./js/detail.js"></script>

<style lang="scss" scoped>
.detail {
.vue-friendly-iframe {
width: 100%;
height: 100%;
-webkit-overflow-scrolling: touch !important;
background-color: #ffffff;
/deep/ .custom-iframe {
height: 100%;
width: 100%;
overflow: auto;
}
}
}
</style>

vue-friendly-iframe npm module is a dynamic async iframes for faster loading iframes. It has several additional attributes for iFrame needs and customization.

  • className: to give class name to iFrame element that can be customized by CSS.
  • crossorigin: to provide support for CORS, defining how the element handles cross-origin requests.
  • target: to specify where the links will open in specific areas of the web browser window.

Security Header for iFrame

To increase child page security so not all web page can embed, we can add Content Security Policy frame-ancestors header. The frame-ancestors directive allows us to specify which parent URLs can frame the current resource. Using the frame-ancestors CSP directive, we can block or allow a page from being placed within a frame or iframe.

If we want to protect our page so it can’t be accessed within a frame, we can add this value on CSP header in child page:

frame-ancestors 'none'

If we only expect page from the same origin that can embed, we can use this value:

frame-ancestors 'self'

We can also specify one or more whitelisted pages that can access by the frame:

frame-ancestors https://a.example.com
frame-ancestors https://a.example.com https://b.example.com

Communicate from child page to parent page

For communicating from child page to parent page, the only way is to use window postMessage() method.

sendMessage (payload) {
// window.parent.postMessage(message, targetOrigin)
const content = window.parent
const stringPayload = JSON.stringify(payload)
content.postMessage(stringPayload, '*')
// targetOrigin = '*' means target window can have any origin
}

Parent page needs to listen the event to handle action.

import { mapGetters } from 'vuex'
import config from '@common/src/config'
import toastUtil from '@common/src/util/toast'
import util from '@common/src/util'

export default {
name: 'MemberPage',
data () {
return {
childUrl: '',
pageHeight: '1024px'
}
},
created () {
this.onIframeLoad()
},
computed: {
...mapGetters(['isMobile']),
iframeHeight () {
return this.isMobile ? window.innerHeight + 'px' : this.pageHeight
},
iframeScrollable () {
return this.isMobile ? 'yes' : 'no'
}
},
methods: {
onIframeLoad () {
window.addEventListener('message', this.eventMessageHandler)
},
eventMessageHandler (event) {
const eventData = event.data
const jsonData = JSON.parse(eventData)
const { pageHeight, type, data, redirectData } = jsonData
const redirectUrl = redirectData && redirectData.redirectUrl
const callbackEventType = config.app.callbackEvent.type
// If the event has pageHeight param, this will be set iFrame height
if (pageHeight) {
this.pageHeight = pageHeight + 'px'
}
// If the event has redirectUrl param, this will be redirect the page to the redirectUrl
if (redirectUrl) {
util.goUrl(redirectUrl)
return
}
if (!type) { return }
// List of action example to handle some event type
switch (type) {
case callbackEventType.REDIRECT_TO_HOME:
util.goUrl(window.location.origin)
break
case callbackEventType.COPY_TO_CLIPBOARD:
navigator.clipboard.writeText(data.clipboardText)
break
case callbackEventType.SCROLL_TO_TOP:
util.scrollToTop()
break
case callbackEventType.SHOW_TOASTER:
toastUtil.open(data)
break
default:
void(0)
}
}
}
}

iFrame Implementation Challenges

Here are some of the challenges that I faced when implementing iFrame.

  1. Back or Forward button issues
    Open a page inside an iFrame will not add page history to the browser navigation. If we have requirement to go back or go forward from child page navigation, make sure child page has their own navigation button.
  2. Difficult tracking router query params
    We will have difficulty to check the full page URL (query params included) that opened inside an iFrame from the browser’s dev tools.
  3. Center element inside iFrame child page
    If we have other element beside our iFrame element in the parent page, it will give impact to the child page element position on the screen. With this condition, if we have element that has center position in the child page, it does not guarantee to be centered too when accessed inside iFrame.
  4. No way to direct link inside iFrame.
    To prevent any security restriction, it’s best to not do redirection to a link directly, especially to a web page with different domain.
  5. No way to bookmark a page inside iFrame.
    We can’t bookmark a page that opened via an iFrame.
  6. iFrame height can’t automatically follow the child page height. We need to set iFrame element height manually from parent page.
  7. Cookies issue
    If we have iFrame component that access a child page with different domain, we can’t access the child page’s JavaScript directly. Thus, we won’t be able to directly transfer cookies from parent page to child page.

References

--

--