I searched for a long time to find a solution to the whole “resize an iframe to fit the content height” problem.  Basically, browsers lock the content server (which provides the iframe src data) away from the parent server (which hosts the iframe) and vice versa.  You cannot access the iframe content at all, on any level, from the parent server.  This is pretty much insurmountable.

So the workaround is to use an HTML5 feature called “postMessage”.  The caveat is that you have to have editing access to the client (remote) webpage. This negates the value if you are trying to show websites like Google; it does allow webmasters to tell their clients how to create their pages to have them show up.

I swiped the code from Josh Fraser, specifically this post, and hacked it up — my only goal is a single communication, so the client code is a modified send feature; the parent only needs to receive the height, so it has a modified receive command.

In order to get the height of the webpage in a cross-browser way, I used the code at the bottom of this webpage as a template, then wrote code to scroll height of the page.

Steps to apply this code:

  1. Copy or link the client Javascript to the client PHP/HTML header.
  2. Add this line to the client PHP doc at the very end of the file.  It sends the height to the parent server.

    echo ‘<script type=”text/javascript”>XD.postHeight();</script>’;

    Alternates:
    jQuery’s $(document).ready() function (need to load jQuery, though)
    HTML body-tag’s onload() event (may not get final page height correctly)

  3. Change the remote URL in the client JS.

    var parentURL = ‘**** paernt URL ****’;

  4. Add the Javascript to the parent (iframe) page HTML (see parent.html for an example).
  5. In the frame-page HTML, change the client URL on the frame-page and set the default additive heights as needed.

    Change these four variables as needed:

    var additiveHt = ****height to add on to the client height****;
    var frameName = ‘**** frame name ****’;
    var clientURL = ‘**** the _server_ URL ****’;;
    var clientPath = ‘**** the _path_ URL ****’;;

  6. (check) Probably have to make sure that all elements on the PHP (client) page have a defined a height and/or min-height style.  Specifically, height/min-height on the html tag and body tab help set the height.

The code for the parent HTML file:


<script type="text/javascript">
/* 
 * Parent (iframe holder) postMessage 'receiving' javascript.
 * (If you want the complete postMessage code, see the links below.)
 * 
 * Modified by Neal Delfeld.
 * Released under the Apache 2.0 license. 
 * 
 * 
 * Other reference info:
 * 
 * A backwards compatable implementation of postMessage.
 * By Josh Fraser (joshfraser.com, http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/)
 * Released under the Apache 2.0 license. 
 *
 * This code was adapted from Ben Alman's jQuery postMessage code found at:
 * http://benalman.com/projects/jquery-postmessage-plugin/
 * 
 * Other inspiration was taken from Luke Shepard's code for Facebook Connect:
 * http://github.com/facebook/connect-js/blob/master/src/core/xd.js
 *
 * The goal of this project was to make a backwards compatable version of postMessage
 * without having any dependency on jQuery or the FB Connect libraries.
 *
 * My goal was to keep this as terse as possible since my own purpose was to use this 
 * as part of a distributed widget where filesize could be sensative.
 * 
 */

/* everything is wrapped in the XD function to reduce namespace collisions */
var XD = function(){

    var interval_id,
    last_hash,
    cache_bust = 1,
    attached_callback,
    window = this;

    return {
        receiveMessage : function(callback, source_origin) {

            /* browser supports window.postMessage */
            if (window['postMessage']) {
                /* bind the callback to the actual event associated with window.postMessage */
                if (callback) {
                    attached_callback = function(e) {
                        if ((typeof source_origin === 'string' && e.origin !== source_origin)
                        || (Object.prototype.toString.call(source_origin) === "[object Function]" && source_origin(e.origin) === !1)) {
                            return !1;
                        }
                        callback(e);
                    };
                }
                if (window['addEventListener']) {
                    window[callback ? 'addEventListener' : 'removeEventListener']('message', attached_callback, !1);
                } else {
                    window[callback ? 'attachEvent' : 'detachEvent']('onmessage', attached_callback);
                }
            } else {
                /* a polling loop is started & callback is called whenever the location.hash changes */
                interval_id && clearInterval(interval_id);
                interval_id = null;

                if (callback) {
                    interval_id = setInterval(function(){
                        var hash = document.location.hash,
                        re = /^#?\d+&/;
                        if (hash !== last_hash && re.test(hash)) {
                            last_hash = hash;
                            callback({data: hash.replace(re, '')});
                        }
                    }, 100);
                }
            }
        }
    };
}();

</script>

<!-- *********************************** the frame *********************************** -->

<iframe
    id='xd_frame'
    src='**** the URL for iframe the content ****'
    style="border:0;width:100%;height=250px;" frameborder="0"
    ></iframe>

<!-- *********************************** end - the frame *********************************** -->

<script type="text/javascript">

    /* height to add on to the client height */
    var additiveHt = 450;
    /* id of the iframe that will be changed */
    var frameName = 'xd_frame';
    /* The _server_ URL for iframe the content.
 * http://site.com
 * *NOT*: http://site.com/webpage.html
 */
    var clientURL = '**** the _server_ URL ****';
    /* The _path_ of iframe the content.
 * /webpage.html
 * *NOT*: http://site.com/webpage.html
 */
    var clientPath = '**** the _path_ for iframe the content ****';;

    /* Backward compatible workaround. */
    frameSrc = clientURL + clientPath + '#' + encodeURIComponent(document.location.href);
    document.getElementById(frameName).src = frameSrc;

    /*
 setup a callback to handle the dispatched MessageEvent. if window.postMessage is supported the passed
 event will have .data, .origin and .source properties. otherwise, it will only have the .data property.
 */
    XD.receiveMessage(
        function(message)
        {
            if( frameSrc.indexOf(clientURL) !== 0 )
            {
                window.alert("Frame source URL invalid.");

                return;
            }

            /* window.alert("---[" + message.data + "]--- received on " + window.location.host); */

            var curHt = parseInt(message.data, 10);
            if(curHt)
            {
                document.getElementById(frameName).style.height = (curHt + additiveHt) + "px";
            }
        },
        clientURL
    );

</script>

The code for the client PHP/HTML file:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr"><head>
<title>iframe test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8;" />
<script type="text/javascript">
    /*
 * Client (iframe content) postMessage 'sending' javascript.
 * (If you want the complete postMessage code, see the links below.)
 *
 * Modified by Neal Delfeld.
 * Released under the Apache 2.0 license.
 *
 *
 * Other reference info:
 *
 * A backwards compatable implementation of postMessage.
 * By Josh Fraser (joshfraser.com, http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/)
 * Released under the Apache 2.0 license.
 *
 * This code was adapted from Ben Alman's jQuery postMessage code found at:
 * http://benalman.com/projects/jquery-postmessage-plugin/
 *
 * Other inspiration was taken from Luke Shepard's code for Facebook Connect:
 * http://github.com/facebook/connect-js/blob/master/src/core/xd.js
 *
 * The goal of this project was to make a backwards compatable version of postMessage
 * without having any dependency on jQuery or the FB Connect libraries.
 *
 * My goal was to keep this as terse as possible since my own purpose was to use this
 * as part of a distributed widget where filesize could be sensative.
 *
 */

    /* everything is wrapped in the XD function to reduce namespace collisions */
    var XD = function(){

        var interval_id,
        last_hash,
        cache_bust = 1,
        attached_callback,
        window = this;

        return {
            postMessage : function(message, target_url, target) {

                if (!target_url) {
                    return;
                }

                target = target || parent;  /* default to parent */

                if (window['postMessage']) {
                    /* the browser supports window.postMessage, so call it with a targetOrigin
 set appropriately, based on the target_url parameter.
 */
                    target['postMessage'](message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1'));

                } else if (target_url) {
                    /* the browser does not support window.postMessage, so set the location
 of the target to target_url#message. A bit ugly, but it works! A cache
 bust parameter is added to ensure that repeat messages trigger the callback.
 */
                    target.location = target_url.replace(/#.*$/, '') + '#' + (+new Date) + (cache_bust++) + '&' + message;
                }
            },

            /* =============================================== */
            /* derived from: http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html */
            /* =============================================== */

            /* Delfeld's addition */
            f_scrollHeight : function()
            {
                return this.f_filterResults (
                    window.scrollHeight ? window.pageYOffset : 0,
                    document.documentElement ? document.documentElement.scrollHeight : 0,
                    document.body ? document.body.scrollHeight : 0
                );
            },
            /* end - Delfeld's addition */

            f_filterResults : function(n_win, n_docel, n_body)
            {
                var n_result = n_win ? n_win : 0;
                if (n_docel && (!n_result || (n_result > n_docel)))
                    n_result = n_docel;
                return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
            },

            /* =============================================== */

            //postHeight : function(parentURL)
            postHeight : function()
            {
                /* The URL for parent webpage (which holds the iframe).
 * e.g., http://site.com/webpage.html
 */
                var parentURL = '**** URL for the parent webpage ****';

                this.postMessage(
                    this.f_scrollHeight(),
                    parentURL,
                    parent
                );
            }
        };
    }();
</script>

</head><body>
<?php 

    // Include any and all PHP HTML-generating code here. . . .
    echo $somePHPStuff;

    // Include this line after all other PHP commands have delivered their content.
    // This could be included elsewhere as well, but should always be called at the end of the page loading.
    echo '<script type="text/javascript">XD.postHeight();</script>';
?>
</body></html>

– 30 –

Advertisements