HTML5 Resize top-level document iframe from inside

2019-01-20 16:57发布

问题:

The problem may seem hard to understand, so I will illustrate it as best I can.

Say I have the following index.html body:

<iframe name="parent"></iframe>

And inside the parent iframe, I load the following page body:

<iframe name="nested-navigation"></iframe>
<iframe name="nested-parent"></iframe>

And inside nested-parent iframe, I load another page. Inside this other page, I need to somehow get to the top-level (index.html) documents parent iframe, so that I can resize the height of its contents using the nested-parent iframe content size.

I correctly resize the index.html's parent iframes height using the loaded contents of the nested pages body. However, in the next nesting, I am unable to reach the index.html document context, and thus am unable to get the parent iframe element.

I need help on figuring out how to reach the index.html's parent iframe. How would I do this?

The page is online and the effects can be viewed here:

www.ngeneersinc.com

If you click the Projects navigation link, it will correctly load the page and resize the parent iframe. Sadly, when you click on the Ngen navigation link inside this nested page, the top-level (index.html) iframe (parent) does not get resized and the content is cut to whatever the height was set to in the previous page.

EDIT:

Essentially, I am trying to do the following in my javascript function:

var e = document.getElementById("nested-parent").contentWindow; // which is OK
var x = e.contentWindow; // which is 'undefined' because I lost the context of the index.html document

回答1:

You can use the following jQuery plugin to accomplish this:
http://benalman.com/projects/jquery-postmessage-plugin/

Then add the following code to a javascript file that you include inside the iframe document and the parent window:

var iFrames = [];
var iFrameCounter = 0;

// Get the parent page URL as it was passed in, for browsers that don't support
// window.postMessage
var parent_url = decodeURIComponent(document.location.hash.replace(/^#/, ''));

// The first param is serialized using $.param (if not a string) and passed to the
// parent window. If window.postMessage exists, the param is passed using that,
// otherwise it is passed in the location hash (that's why parent_url is required).
// The second param is the targetOrigin.
function SetHeight() {
    var id = Number(parent_url.replace(/.*#(\d+)(?:&|$)/, '$1'));
    $.postMessage({ if_height: $(document).outerHeight(true), id: id }, parent_url, parent);
};

// Adds the iframe for the given url to the given container
function CreateFrame(container, url) {
    $(function () {
        // Pass the parent page URL into the Iframe in a meaningful way
        var src = url + '#' + encodeURIComponent(document.location.href + "#" + iFrameCounter);

        // Append the iFrame into the DOM.
        var html = '<iframe id="iFrame' + iFrameCounter + '" src="' + src + '" width="100%" scrolling="no" frameborder="0">Your browser lacks support for iFrames!<\/iframe>';
        iFrames[iFrameCounter] = { id: "iFrame" + iFrameCounter, container: container, if_height: 0 };

        // Add iFrame to DOM
        $(container).append($(html));
        iFrameCounter++;
    });
}

$(function () {
    // Setup a callback to handle the dispatched MessageEvent event. In cases where
    // window.postMessage is supported, the passed event will have .data, .origin and
    // .source properties. Otherwise, this will only have the .data property.
    $.receiveMessage(function (e) {

        // Get frameId
        var id = Number(e.data.replace(/.*id=([\d-]*).*/, '$1'));

        var frame = iFrames[id];

        // Get the height from the passsed data.
        var h = Number(e.data.replace(/.*if_height=([\d-]*).*/, '$1'));

        if (!isNaN(h) && h > 0 && h !== frame.if_height) {
            // Height has changed, update the iframe.
            $("#" + frame.id).css("height", (frame.if_height = h));
            $("#" + frame.id).css("min-height", (frame.if_height = h));
            $("#" + frame.id).css("max-height", (frame.if_height = h));

            // Also update the parent element of the iframe.
            $("#" + frame.id).parent().css("height", (frame.if_height = h));
            $("#" + frame.id).parent().css("min-height", (frame.if_height = h));
            $("#" + frame.id).parent().css("max-height", (frame.if_height = h));
        }
    });
});

Then use the following code to create the iframe in the parent window:

<script type="text/javascript">
     $(document).ready(function () {
         CreateFrame("#someiFrameContainer", url);
     });
</script>
<div id="someiFrameContainer"></div>

Then everytime the height of the document inside the iFrame changes call this function from the iFrame page:

SetHeight();

This will cause the iframe page to send a message containing its new total height to its parent window (supports cross-domain), which will catch the message and update the size of the iframe. This requires that the parent window also includes the script file from above, as this file registers the events and functions to handle these messages automatically when using CreateFrame();