Content that is presented in an iFrame appears with a vertical scrollbar if the length of the child document exceeds the height of the iFrame and with a horizontal scrollbar if the child document elements won’t wrap to the width of the iFrame. There are ways of overriding this behavior, but not without the risk of having scrolbars when not needed, or worse, no scrollbars when required. Sometimes the solution is to set the dimensions of the iFrame to that of the containing document so that scrollbars are not needed. A great idea in theory, but the devil’s in the implementation details here. Provided you’ve got access to the iFrame content, you can pull it off. This tutorial shows you how it’s done.
Using CSS
Originally, iFrame scrollbars were set using the scrolling attribute. It could be set to “auto”, which means that scrollbars appear if needed (the default behavior), “yes” (scrollbars are always shown, whether needed or not), and “no” (scrolbars are never shown, even when needed).
Although still supported in all major browsers, the scrolling attribute has largely been eclipsed by the CSS overflow, overflow-x, and overflow-y CSS properties. It replaced the “yes” and “no” values with “scroll” and “hidden” (“auto” remained), while offering additional values of “visible”, “initial”, and “inherit”.
It would seem evident that if you wanted to show a vertical scrollbar and suppress the horizontal one you would apply a style of “overflow-y:scroll; overflow-x:hidden” to the <IFRAME> element. However in practice, this doesn’t have any effect. Instead, you have to apply the CSS values to the child document’s <HTML> or <BODY> tag. In essence, the child document is telling the parent how to display it. If you want it the other way around, you have to apply the rules dynamically via scripting, for instance:
function setIframeSize(iframe) { var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; var iframeBody; if (iframeDocument) { iframeBody = iframeDocument.querySelector('body'); iframeBody.style.overflowY = 'scroll'; iframeBody.style.overflowX = 'hidden'; } }
Internet Explorer uses the contentWindow property while most other browsers recognize the contentDocument property. That is why we check for this property first in the OR condition.
Document Communication via window.postMessage()
Documents on different pages are only allowed to communicate between each other if their domains, protocols and ports are the same. That is, unless the browser supports the HTML5 window.postMessage() function, which provides cross-domain communication between scripts.
Here is the syntax:
window.postMessage(message, targetOrigin, [transfer]);
The message argument can be just about anything, including objects, arrays, and native types (strings, numbers, Dates, etc).
The targetOrigin value must be either an asterisk “*”, slash “/”, or absolute URL. Otherwise a SyntaxError exception will be thrown. As a rule of thumb, if you know the window location, you should provide this specific location instead of just putting an asterisk “*”.
The optional transfer parameter is sent along with the message, and then becomes unusable on the sending side.
In the iFrame document, there is an event handler attached to the body’s onload event. In the handler, the body’s height and width are calculated and sent to the parent document by invoking the parent’s postMessage() function:
<!DOCTYPE HTML> <html> <head> <title>Child Page</title> <script type="text/javascript"> function resizeIFrame(){ if (parent.postMessage) { var body = document.body, html = document.documentElement, loc = document.location, height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight), width = Math.max(body.scrollWidth, body.offsetWidth, //add a bit of margin html.clientWidth, html.scrollWidth, html.offsetWidth) + 5; parent.postMessage({ h: height, w: width }, loc.protocol + '//' + loc.host); } } </script> </head> <body onload="resizeIFrame();" style="white-space: nowrap;"> <p>this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2</p> <p>this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2</p> <p>this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2this is child2</p> <!-- on and on... --> </body> </html>
In the parent document, we need to attach an event handler to the postMessage event that accepts an event as its argument. Inside the function, the first line verifies the sender’s identity using the origin property in order to thwart cross-site scripting attacks. The iFrame’s dimensions are reset according the the event data’s w and h properties – that’s the first argument passed to the postMessage() function above:
<!doctype html> <html> <head> <title>Document A</title> <meta charset="utf-8"> </head> <body style="white-space: nowrap;"> <iframe id="myIFrame" width="300" height="200" src="child.html" frameborder="1"></iframe> <script type="text/javascript"> var resizeIFrame = function(event) { var loc = document.location; if (event.origin != loc.protocol + '//' + loc.host) return; var myIFrame = document.getElementById('myIFrame'); if (myIFrame) { myIFrame.style.height = event.data.h + "px"; myIFrame.style.width = event.data.w + "px"; } }; if (window.addEventListener) { window.addEventListener("message", resizeIFrame, false); } else if (window.attachEvent) { window.attachEvent("onmessage", resizeIFrame); } </script> </body> </html>
Conclusion
There are a number of ways to set scrollbar options on an iFrame, from simple to complex. The course of action that you select will depend greatly on how much of a priority this issue is to you.