How to Embed resizable iframes in Website Pages

Introduction

This guide provides a method to allow <iframe> content to be embedded in website pages such that it can be expanded. This is especially useful for maps and large tables of data where the reader will want to see the content in the context of the website and then expand it to see details of the information presented within the <iframe>.

This guide is aimed at web developers so it does not provide a detailed explanation of how it works. Also, the code requires thorough testing for each website it is included in. It may need to be altered to fix bugs and modify behaviour on an as required basis.

Host Page Changes

Javascript and Stylesheets

Add the following code to the <head> section of the pages requiring the expandable <iframe> content. Often this involves modifying the website template.

<link rel="stylesheet" type="text/css" href="https://yui.yahooapis.com/combo?2.9.0/build/container/assets/skins/sam/container.css&2.9.0/build/resize/assets/skins/sam/resize.css"/>
<script type="text/javascript" src="https://yui.yahooapis.com/combo?2.9.0/build/yahoo-dom-event/yahoo-dom-event.js&2.9.0/build/dragdrop/dragdrop-min.js&2.9.0/build/container/container-min.js&2.9.0/build/element/element-min.js&2.9.0/build/resize/resize-min.js"></script>
<link rel="stylesheet" href="css/resizableFrame.css" type="text/css" media="screen" charset="utf-8" />
<script src="js/resizableFrame.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
/*
  margin and padding on body element
  can introduce errors in determining
  element position and are not recommended;
  we turn them off as a foundation for YUI
  CSS treatments. 
*/
body {
    margin:0;
    padding:0;
}
</style>

Frame Container

Add the following code to the appropriate location within the pages that the expandable <iframe> content is to appear in.

<div class="frameHomeContainer" style="width:500px; height:530px">
	<div id="resizableFramePanel" class="resizableFramePanel">
		<div class="hd">Map Showing Parks</div>
		<iframe src="your iframe URL" 
				width="100%" 
				height="100%" 
				frameborder="0" 
				border="0" 
				allowtransparency="yes"
				scrolling="no"
				allowFullScreen> 
			<p>A message presented if the user's browser does not support iframes - this is unlikely.</p> 
		</iframe>
		</div>
	</div>
</div>

resizableFrame.css

Here is the source code of resizableFrame.css.

.frameHomeContainer {
	margin-right: 10px;
}
.resizableFramePanel .ft {
	height:15px;
	padding:0;
}
.resizableFramePanel .yui-resize-handle-br {
	right:0;
	bottom:0;
	height: 8px;
	width: 8px;
	position:absolute;
}
.resizableFramePanel_c.hide-scrollbars .yui-resize .bd {
	overflow: hidden;
}
.resizableFramePanel_c.show-scrollbars .yui-resize .bd {
	overflow: auto;
}
.resizableFramePanel_c.show-scrollbars .underlay {
	overflow: visible;
}
.yui-skin-sam .container-expand {
	position:absolute;
	top:1px;
	right:6px;
	width:26px;
	height:18px;
	background:url(../images/expand-frame.png) no-repeat 0 0px;
	cursor:pointer
}
.yui-skin-sam .container-restore {
	position:absolute;
	top:1px;
	right:34px;
	width:26px;
	height:18px;
	background:url(../images/restore-frame.png) no-repeat 0 0px;
	cursor:pointer
}

resizableFrame.js

Here is the source code of resizableFrame.js.

/**
This code initialises an iframe and surrounding containers assuming the following structure:

	<div id="mapSizer" style="background:#eee; margin-right: 10px; width:500px; height:530px">
		<div id="resizableFramePanel" class="resizableFramePanel">
			<div class="hd">Map Title</div>
			<iframe ...> 
			</iframe>
			</div>
		</div>
	</div>
	
	To use a different id than 'resizableFramePanel', add the following JavaScript:
	
	var mapConfig = {resizableFramePanelId: 'resizableFramePanel'};
 */
YAHOO.util.Event.onDOMReady(

	function () {
		var mapConfig = window.mapConfig || {};
		if (typeof(mapConfig.resizableFramePanelId) == undefined || mapConfig.resizableFramePanelId == null) {
			mapConfig.resizableFramePanelId = 'resizableFramePanel';
		}
		var resizableFramePanel = null;
		var mapResize = null;

		try {
		
			// Create a panel Instance, from the 'resizableFramePanel' DIV standard module markup
			resizableFramePanel = new YAHOO.widget.Panel(mapConfig.resizableFramePanelId, {
				draggable: true,
				close: false,
				width: "400px",
				height: "400px",
				autofillheight: "body",
				constraintoviewport: false,
				context: ["showbtn", "tl", "bl"]
			});
			resizableFramePanel.render();

			addMapToolbarItems();
			
			// Create Resize instance, binding it to the 'resizableFramePanel' DIV 
			var resize = new YAHOO.util.Resize(mapConfig.resizableFramePanelId, {
				handles: ["br"],
				autoRatio: false,
				minWidth: 500,
				minHeight: 100,
				status: false 
			});
			mapResize = resize;

			// Setup startResize handler, to constrain the resize width/height
			// if the constraintoviewport configuration property is enabled.
			resize.on("startResize", function(args) {

				if (this.cfg.getProperty("constraintoviewport")) {
					var D = YAHOO.util.Dom;

					var clientRegion = D.getClientRegion();
					var elRegion = D.getRegion(this.element);

					resize.set("maxWidth", clientRegion.right - elRegion.left - YAHOO.widget.Overlay.VIEWPORT_OFFSET);
					resize.set("maxHeight", clientRegion.bottom - elRegion.top - YAHOO.widget.Overlay.VIEWPORT_OFFSET);
				} else {
					resize.set("maxWidth", null);
					resize.set("maxHeight", null);
				}

			}, resizableFramePanel, true);

			// Setup resize handler to update the Panel's 'height' configuration property 
			// whenever the size of the 'resizableFramePanel' DIV changes.

			// Setting the height configuration property will result in the 
			// body of the Panel being resized to fill the new height (based on the
			// autofillheight property introduced in 2.6.0) and the iframe shim and 
			// shadow being resized also if required (for IE6 and IE7 quirks mode).
			resize.on("resize", function(args) {
				try {
					var panelHeight = args.height;
					this.cfg.setProperty("height", panelHeight + "px");
				
					// Now ensure the map content handles resizing...
					
					// Now ensure the map content handles resizing...
					var mapContainer = getMapContainer();
					var mapFrame = findIframe(mapContainer);
					
					//var newFrameHeight = cmsMapContainer.offsetHeight - 5;
					var newFrameHeight = panelHeight - 40;
					mapFrame.height = newFrameHeight + "px";
				} catch (e) {
					alert("On resize error: " + e);
				}
			}, resizableFramePanel, true);
			
			restoreFrame();
		
		} catch (e) {
			alert(e);
		}
	
		function getMapContainerPositionAndDimensions() {
			var mapContainer = getMapContainer();
			var mapContainerOffset = getOffset(mapContainer);
			return {
				x: mapContainerOffset.left,
				y: mapContainerOffset.top,
				width: mapContainer.offsetWidth,
				height: mapContainer.offsetHeight};
		}
		function getMapContainer() {
			var mapPanel = document.getElementById(mapConfig.resizableFramePanelId);
			if (mapPanel == null) {
				alert("The " + mapConfig.resizableFramePanelId + " element was not found.");
				return null;
			} else {
				var mapContainer = mapPanel.parentNode;
				if (mapContainer.id = "resizableFramePanel_c") {
					mapContainer = mapContainer.parentNode;
				}
				return mapContainer;
			}
		}
		function findIframe(parent) {
			if (parent == null) {
				return null;
			} else {
				var iframes = document.getElementsByTagName("iframe");
				for (var i = 0; i < iframes.length; i++) {
					var iframe = iframes[i];
					var iframeParent = iframe.parentNode;
					if (iframeParent != null && iframeParent.id == mapConfig.resizableFramePanelId) {
						return iframe;
					}
				}
				return null;
			}
		}
		function getOffset(el) {
			var _x = 0;
			var _y = 0;
			while ( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
				_x += el.offsetLeft - el.scrollLeft;
				_y += el.offsetTop - el.scrollTop;
				el = el.offsetParent;
			}
			return {left: _x, top: _y};
		}
		function getWindowDimensions() {
			var dimensions = new Object();
			dimensions.width = 630;
			dimensions.height = 460;
			if (document.body && document.body.offsetWidth) {
				dimensions.width = document.body.offsetWidth;
				dimensions.height = document.body.offsetHeight;
			}
			if (document.compatMode=='CSS1Compat' && document.documentElement && document.documentElement.offsetWidth) {
				dimensions.width = document.documentElement.offsetWidth;
				dimensions.height = document.documentElement.offsetHeight;
			}
			if (document.body.clientWidth && document.body.clientHeight) {
				dimensions.width = document.body.clientWidth;
				dimensions.height = document.body.clientHeight;
			}
			if (window.innerWidth && window.innerHeight) {
				dimensions.width = window.innerWidth;
				dimensions.height = window.innerHeight;
			}
			return dimensions;
		}
		function resizeMapFrame(expandFrame) {
			var panelContainer = document.getElementById('resizableFramePanel_c');
			var resizableFramePanelDiv = document.getElementById(mapConfig.resizableFramePanelId);
			var expandLink = document.getElementById('expandFrame');
			var restoreLink = document.getElementById('restoreFrame');
			var panelX = -1;
			var panelY = -1;
			var panelWidth = 0;
			var panelHeight = 0;
			if (expandFrame) {
				panelContainer.style.left = '0px';
				panelContainer.style.top = '0px';
				var paddingWidth = 14;
				var paddingHeight = 0;
				var panelToolbarHeight = 18;
				var panelFooterHeight = 6;
				var dimensions = getWindowDimensions();
				panelWidth = dimensions.width - 2 * paddingWidth;
				panelHeight = dimensions.height - panelToolbarHeight - panelFooterHeight - 2 * paddingHeight;
				panelX = (dimensions.width - panelWidth - paddingWidth) / 2;
				panelY = (dimensions.height - panelHeight - panelToolbarHeight) / 2;
				resizableFramePanelDiv.style.left = '0px';
				resizableFramePanelDiv.style.top = '0px';
				// Increase the z-index to ensure it is above all other content...
				resizableFramePanel.cfg.setProperty("zIndex", 1000); 
			} else {
				panelContainer.style.left = '278px';
				panelContainer.style.top = '248px';
				var dimensions = getMapContainerPositionAndDimensions();
				panelX = dimensions.x;
				panelY = dimensions.y;
				panelWidth = dimensions.width;
				panelHeight = dimensions.height;
				resizableFramePanelDiv.style.left = '0px';
				resizableFramePanelDiv.style.top = '0px';
				// Restore the z-index to allow menus to float over the map...
				resizableFramePanel.cfg.setProperty("zIndex", 1);
			}
			
			resizableFramePanelDiv.style.width = panelWidth + 'px';
			resizableFramePanelDiv.style.height = panelHeight + 'px';
			mapResize.fireEvent('beforeResize');
			mapResize.fireEvent('startResize');
			mapResize.fireEvent('resize', {width: panelWidth, height: panelHeight});
			resizableFramePanel.moveTo(panelX, panelY);
		}
		function expandFrame() {
			try {
				resizeMapFrame(true);
			} catch (e) {
				alert("Move error: " + e);
			}
		}
		function restoreFrame() {
			try {
				resizeMapFrame(false);
			} catch (e) {
				alert("Move error: " + e);
			}
		}
		function addMapToolbarItems() {
			var resizableFramePanelDiv = document.getElementById(mapConfig.resizableFramePanelId);
			if (resizableFramePanelDiv) {
				var expandLink = document.createElement('a');
				expandLink.className = 'container-expand';
				expandLink.setAttribute('id', 'expandFrame');
				expandLink.setAttribute('href', '#');
				expandLink.onclick=expandFrame;
				resizableFramePanelDiv.insertBefore(expandLink, resizableFramePanelDiv.firstChild);
	
				var restoreLink = document.createElement('a');
				restoreLink.className = 'container-restore';
				restoreLink.setAttribute('id', 'restoreFrame');
				restoreLink.setAttribute('href', '#');
				restoreLink.onclick=restoreFrame;
				resizableFramePanelDiv.insertBefore(restoreLink, resizableFramePanelDiv.firstChild);
			}
		}
			
	}

);