Skip to content

Instantly share code, notes, and snippets.

@mems
Last active Nov 26, 2018
Embed
What would you like to do?
Light version Iframe resizer
/*
IFrame Resizer light version
Based on [iframe-resizer](https://github.com/davidjbradshaw/iframe-resizer)
- this version lib to have the base/minimal features
- only use default values
- force resize the iframe when parent window is resized
To avoid memory leaks, only iframes (with a specific classname) that are attached to the document are handled
Window events listeners are shared.
If the iframe is removed from document (detached nodes) subsequent message events are ignored
*/
(function(window, document){
var IFRAME_RESIZER = "js-iframe-resizer";
var IFRAME_RESIZER_PREFIX = "[iFrameSizer]";
var CONFIG_SYMBOL = window.Symbol ? Symbol("iframeresizer") : "_iframeResizer" + Math.round(Math.random() * 1e10);
// live collection (this will be updated automaticaly)
var counter = 0;
var currentIFrames = [];// cache of initialized iframes, will be update in the next animation frame
var iframes = document.getElementsByClassName(IFRAME_RESIZER);
window.addEventListener("resize", debounceRequestIFrameResize);
window.addEventListener("message", messageHandler);
function debounceRequestIFrameResize(){
// RAF is used for debounce to next animation frame
requestAnimationFrame(requestIFrameResize);
}
// Resize message
function requestIFrameResize(){
var message = ["resize"];
for(var i = 0; i < currentIFrames.length; i++){
var iframe = currentIFrames[i];
var config = iframe[CONFIG_SYMBOL];
// If the channel is not open, wait for it
if(config.connectionState !== "open"){
return;
}
// <prefix>resize
// https://github.com/davidjbradshaw/iframe-resizer/blob/master/src/iframeResizer.contentWindow.js#L1076-L1084
postMessage(iframe, message, 100);// 100ms should be enough for postMessage roundtrip (window->iframe+iframe->window)
}
}
function sendInitMessage(iframe){
var config = iframe[CONFIG_SYMBOL];
// Init message
// https://github.com/davidjbradshaw/iframe-resizer#options
// <prefix><iframe_id>:<body_margin_v1>:<calculate_width>:<logging>:<interval>:<auto_resize>:<body_margin>:<height_calc_mode>:<body_background>:<body_padding>:<tolerance>:<in_page_links>:<resize_from>:<width_calc_mode>
// Example: [iFrameSizer]myIFrameID:8:false:false:32:true:true:null:bodyOffset:null:null:0:false:parent:scroll
// https://github.com/davidjbradshaw/iframe-resizer/blob/da4ac20121304f7646ca0c888a4a885e0675380a/src/iframeResizer.contentWindow.js#L194-L216
// https://github.com/davidjbradshaw/iframe-resizer/blob/268d3733ae3758e103383ba242d5bf6d8cdbb11a/src/iframeResizer.js#L690-L706
// Since the script is retro compatible, we can send only a part of the message, others values will be defaulted
// <prefix><iframe_id>:<body_margin_v1>:<calculate_width>
// https://github.com/davidjbradshaw/iframe-resizer/blob/da4ac20121304f7646ca0c888a4a885e0675380a/src/iframeResizer.contentWindow.js#L1070-L1074
postMessage(iframe, [config.id, 8, false], 5000);// 5s that let the iframe resize to be loaded and initialized
}
/**
* Send message to iframe
* @param {HTMLIFrameElement} iframe
* @param {Array} message
* @param {number} [timeout] If provided, the target must respond within the given time
*/
function postMessage(iframe, message, timeout){
var config = iframe[CONFIG_SYMBOL];
clearTimeout(config.messageTimeoutID);
if(timeout){
config.messageTimeoutID = setTimeout(messageTimeoutHandler.bind(null, iframe), timeout);
}
iframe.contentWindow.postMessage(IFRAME_RESIZER_PREFIX + message.join(":"), config.origin);
}
/**
* Initialize iframe
* Try to contact iframeResizer other side
* @param {HTMLIFrameElement} iframe
*/
function initializeIFrame(iframe){
var config = iframe[CONFIG_SYMBOL] = {
id: iframe.id || "iframeResizer" + (++counter),
origin: (iframe.src.match(/^https?:\/\/[^\/]+/) || ["null"])[0],
connectionState: "init",
initIntervalID: 0,
messageTimeoutID: 0,
};
config.initIntervalID = setInterval(sendInitMessage.bind(null, iframe), 100);// try to contact the other side
}
/**
* The iframeReizer doesn't respond before timeout
* @param iframe
*/
function messageTimeoutHandler(iframe){
clearIFrameTimers(iframe);
iframe[CONFIG_SYMBOL].connectionState = "close";
}
function clearIFrameTimers(iframe){
var config = iframe[CONFIG_SYMBOL];
clearInterval(config.initIntervalID);
clearTimeout(config.messageTimeoutID);
}
function messageHandler(event){
for(var i = 0; i < currentIFrames.length; i++){
var iframe = currentIFrames[i];
var config = iframe[CONFIG_SYMBOL];
// Test if it's not from that iframe
if(iframe.contentWindow !== event.source && iframe.contentWindow !== event.source.parent){
continue;
}
var data = String(event.data);
// Message doesn't come from iframeresizer lib, ignore it
if(data.slice(0, IFRAME_RESIZER_PREFIX.length) !== IFRAME_RESIZER_PREFIX){
return;
}
config.connectionState = "open";
clearIFrameTimers(iframe);
// <prefix><iframe_id>:<height>:<width>:<event>
// https://github.com/davidjbradshaw/iframe-resizer/blob/da4ac20121304f7646ca0c888a4a885e0675380a/src/iframeResizer.contentWindow.js#L993-L1000
data = data.slice(IFRAME_RESIZER_PREFIX.length).split(":");
// Is not the same origin, that means the iframe has been redirected
if(event.origin !== config.origin || data[0] !== config.id){
// Reinitialize
initializeIFrame(iframe);
return;
}
iframe.style.height = data[1] + "px";
// Only one iframe at a time
// End
return;
}
}
// Cheap DOM mutation observer: works everywhere (IE10+) and better performance (use live collection)
function initResizableIFrames(){
var previousIFrames = currentIFrames;
currentIFrames = Array.prototype.slice.call(iframes);
var i;
var iframe;
// Search for newly attached iframes
for(i = 0; i < currentIFrames.length; i++){
iframe = currentIFrames[i];
// Already exist
if(previousIFrames.indexOf(iframe) >= 0){
continue;
}
initializeIFrame(iframe);
}
// Search for newly detached iframes
for(i = 0; i < previousIFrames.length; i++){
iframe = previousIFrames[i];
// Still exist
if(currentIFrames.indexOf(iframe) >= 0){
continue;
}
clearIFrameTimers(iframe);
iframe[CONFIG_SYMBOL] = undefined;// better for perf instead of "delete"
}
requestAnimationFrame(initResizableIFrames);
}
initResizableIFrames();
})(window, document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment