Last active
November 26, 2018 11:13
-
-
Save mems/d135e18d1375b0fc39232e662e7abbd2 to your computer and use it in GitHub Desktop.
Light version Iframe resizer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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