Using iframes in a responsive page can be frustrating. It’s easy enough to make an iframe’s width span 100% of its container, but sizing its height is tricky — especially if the content of the iframe changes height depending on page width (for example, because of text wrapping or media queries) or events within the iframe.
Pym.js embeds and resizes an iframe responsively (width and height) within its parent container. It also bypasses the usual cross-domain issues.
Use case: The NPR Visuals team uses Pym.js to embed small custom bits of code (charts, maps, etc.) inside our CMS without CSS or JavaScript conflicts. See an example of this in action.
pym.js does not require jQuery or any other libraries. Some of the examples use jQuery on the child pages for unrelated features.http://blog.apps.npr.org/pym.js/examples/graphic/child.htmlpym.js isn't built to handle embedding more than a handful of child frames on the parent. Ten or fewer is a good rule of thumb.pym.js has been tested in:
Internet Explorer versions earlier than 9 are not supported.
If you use Bower to manage your Javascript dependencies then you can install Pym.js by running:
bower install pym.js
Otherwise you can just copy pym.js into your codebase like you would any other javascript library.
If you use NPM to manage your Javascript dependencies then you can install Pym.js by running:
npm install pym.js
Otherwise you can just copy pym.js into your codebase like you would any other javascript library.
Resize your browser window to see the responsiveness in action.
pym.js. Only once per page, no matter how many iframes you will have.new pym.Parent(parent_id, child_url); for each responsive iframe. (Pym will generate the actual iframe element.)pym.Parent('example', 'child.html', { xdomain: '*\.npr\.org' });<div id="example"></div>
<script type="text/javascript" src="pym.js"></script>
<script>
var pymParent = new pym.Parent('example', 'child.html', {});
</script>
You don’t have to do anything special to the child pages, but you do need to keep a few things in mind for the parent page:
<div id="example">, <div id="bar-graph">, etc.).pym.js only once on the page.<div id="example-1"></div>
<div id="example-2"></div>
var pymParent = new pym.Parent('example-1', 'child-1.html', {});
var second = new pym.Parent('example-2', 'child-2.html', {});
pym.js.new pym.Child();.pym.Child({ renderCallback: myFunc }); This function will be called once when the page loads and again any time the window is resized.pym.Child({ polling: 500 });.pymChild.sendHeight() at any time to force the iframe to update its size.In our simplest example, we have an HTML table that changes format (via CSS media queries). The height of the iframe adjusts to the height of the content onload and onresize.
var pymChild = new pym.Child();
This might be useful in cases where sections of your child page need to be redrawn based on the new width. (For example, a graphic generated by D3, which would not stretch or reflow on its own.)
function drawGraphic(width) {
...
}
var pymChild = new pym.Child({ renderCallback: drawGraphic });
If you have dynamic content and need finer control over resize events, you can invoke pymChild.sendHeight() in the child window at any time to force the iframe to update its size. For example, say you have a quiz, and the content of the page changes when someone selects an answer, affecting the page’s height:
function check_answer(e) {
// highlight the correct answer
...
// send updated height to parent
pymChild.sendHeight();
}
$('.question').find('li').on('click', check_answer);
var pymChild = new pym.Child();
If you have links in your iframe that lead to other pages, you can maintain auto-resizing on the subsequent pages by explicitly setting the child ID in each one.
var pymChild = new pym.Child({ id: 'example-follow-links' });
The Pym.js library and a small bit of javascript are injected onto the parent page. This code writes an iframe to the page in a container of your choice. The request for the iframe’s contents includes querystring parameters for the initialWidth and childId of the child page. The initialWidth allows the child to know its size immediately on load, because in iOS, the child frame can not determine its own width accurately. The childId allows multiple children to be embedded on the same page, each with its own communication to the parent.
The child page also includes Pym.js and its own javascript. It initializes cross-iframe communication to the parent, renders any dynamic content and then sends the computed height of the child to the parent via postMessage. Upon receiving this message the parent resizes the containing iframe, thus ensuring the contents of the child are always visible.
The parent page also registers for resize events. Any time one is received, the parent sends the new container width to each child via postMessage. The child re-renders its content and sends back the new height.
Certain CMSes prevent custom Javascript from being embedded on the page. In order to allow pym to support those environments, it can be initialized via data- attributes. This limits usage of pym to simple cases, but should still be enough for many users.
(NPR member stations: Use this minor workaround to get this to work in Core Publisher.)
<div data-pym-src="child.html"></div>
Although not its original purpose, pym.js can also be used to send and receive generic event data between the iframe parent and child. A simple, jQuery-like event binding model is used to support this message passing.
In the following example a click on the link in the parent container will navigate the child frame to a new location.
var pymParent = pym.Parent('example', 'child.html', {});
document.getElementById('myLink').addEventListener('click', onLinkClick);
function onLinkClick(e) {
e.preventDefault();
pymParent.sendMessage('navigate', e.target.href);
}
var pymChild = new pym.Child();
pymChild.onMessage('navigate', onNavigateMessage);
function onNavigateMessage(url) {
window.location.href = url;
}
Note that the reverse scenario can be performed as well; children can send messages to the parent with the same sendMessage() function.
As scrolling and navigating the parent window is a very common need, there are shortcuts for these actions that do not require you to implement your own events.
pymChild.scrollParentTo('about');
pymChild.navigateParentTo('https://github.com/nprapps/pym.js');