[ Team LiB ] Previous Section Next Section

14.3 Including External HTML Content

NN 6, IE 5

14.3.1 Problem

You want to combine (include) content from another HTML document into a single document on the page.

14.3.2 Solution

Put the external content into an iframe element, and disguise the iframe so that it looks to be part of the regular document flow. Here is an example of an iframe element that blends seamlessly into a plain HTML page:

<iframe id="myFrame" frameborder="0" vspace="0" hspace="0" marginwidth="0" 
marginheight="0" width="100%" src="external.html" scrolling="no" 
style="overflow:visible"></iframe>

To size the iframe element correctly, you must wait for the content to load, and then use scripts to find out the content height. The following function is invoked by the onload event handler of the page, which passes the ID of the iframe to be adjusted:

function adjustIFrameSize(id) {
    var myIframe = document.getElementById(id);
    if (myIframe) {
        if (myIframe.contentDocument && myIframe.contentDocument.body.offsetHeight) {
            // W3C DOM (and Mozilla) syntax
            myIframe.height = myIframe.contentDocument.body.offsetHeight;    
        } else if (myIframe.Document && myIframe.Document.body.scrollHeight) {
            // IE DOM syntax
            myIframe.height = myIframe.Document.body.scrollHeight;
        }
    }
}

For the example iframe element, the onload event handler is:

onload = "adjustIFrameSize('myFrame');"

The user will see the page rearrange itself when the iframe resizes.

14.3.3 Discussion

For the resizing script to work, both the host and external pages must be served from the same domain and server, to allow the script to dive into the otherwise protected realm of the iframe's content. Notice that the reference to the content of the iframe element is not as direct as you might be accustomed to from working with framesets. If you begin with a reference to the iframe element, you then need the DOM-compatible syntax to reach the document object within that frame: Document for IE and contentDocument for W3C DOM-based browsers. References to those documents point to the root document containing the content visible in the frame. From there, you can reach the body or other elements within the document. In the case of the embedded iframe, the total height of the rendered content (unknown until it loads) governs the ultimate height of the iframe.

Be aware that hyperlinks in the iframe's content will load their destination documents into the iframe, unless the targets of those links are set to _top. Also, any new content loaded into the iframe that is not the same height as the original either leaves a gap (too short) or is clipped (too long). The only fix is to resize the iframe for the new content.

To prepare the iframe for automatic resizing, you must bind an onload event handler to the iframe element. While a hardwired onload event handler or event property assignments may not work for the iframe element in Netscape 6 or later, more modern event binding does. Even so, you need to branch for the event-model specific ways of binding elements. Revise the function shown in the Solution to bind the events as follows:

function adjustIFrameSize(id) {
    var myIframe = document.getElementById(id);
    if (myIframe) {
        if (myIframe.contentDocument && myIframe.contentDocument.body.offsetHeight) {
            // W3C DOM (and Mozilla) syntax
            myIframe.height = myIframe.contentDocument.body.offsetHeight;    
        } else if (myIframe.Document && myIframe.Document.body.scrollHeight) {
            // IE DOM syntax
            myIframe.height = myIframe.Document.body.scrollHeight;
        }
        // bind onload events to iframe
        if (myIframe.addEventListener) {
            myIframe.addEventListener("load", resizeIframe, false);
        } else {
            myIframe.attachEvent("onload", resizeIframe);
        }
   }
}

The events invoke a function that processes the event to pass the desired information (the ID of the iframe) back to the adjustIFrameSize( ) function:

function resizeIframe(evt) {
    evt = (evt) ? evt : event;
    var target = (evt.target) ? evt.target : evt.srcElement;
    // take care of W3C event processing from iframe's root document
    if (target.nodeType =  = 9) {
      if (evt.currentTarget && 
          evt.currentTarget.tagName.toLowerCase( ) =  = "iframe") {
            target = evt.currentTarget;    
        }
    }
    if (target) {
        adjustIFrameSize(target.id);
    }
}

The tricky part is that the onload event fires for the content document of the iframe in W3C DOM browsers rather than for the iframe element directly. Fortunately, the event bubbles up to the containing iframe, and the W3C DOM event object's currentTarget property gives us a reference to the element that is actually processing the event, regardless of original target. The event bindings are invoked again in adjustIFrameSize( ) function, but there is no harm in doing so. If you have some other initializations on the page that occur in response to the main page's onload event handler, you can shift these assignments to that initialization function so they execute only one time.

14.3.4 See Also

Recipe 9.1 for handling conflicting event models together; Recipe 9.2 for details about the onload event handler.

    [ Team LiB ] Previous Section Next Section