In a section above we demonstrated that it is possible to open a new browser window, and to dynamically create a HTML document within that new window. This is a very powerful technique in JavaScript, and it applies not only to new browser windows, but also to frames. In fact, it is much more common to create a web site that uses multiple frames than it is to create one that uses multiple browser windows. The key to successful programming with frames is knowing how to refer to one frame from another. Recall that every Window object (which means every browser window, and every frame within a window or within another frame) has a frames[] array, and also parent, top, self, and window properties. You might want to refer back to Figure 12-1 to refresh your memory about how each of these properties work.
Once you know how to refer to any frame from any other frame, you can start writing JavaScript programs that work in complex framed documents. Pay careful attention to how you name frames, and be aware of what window any given piece of code is running in. For example, if an event handler in frame A invokes a function that is defined in frame B, the code in that function is running in frame A, not frame B--and if the code wants to refer to frame B, it can't just use the implicit window reference, as it could if it were actually running in frame B. When you encounter complexities like these, it is helpful to give each frame a name, and refer to them by name rather than by number. (Recall that giving a frame a name creates a property with that name in the frame's parent.) When you are working with frames that are nested, at multiple levels, however you may want to create some "global" properties of the top-level browser window that refer to each of the frames in your program, no matter how many levels down they are nested. Then, for example, you can refer to frames with expressions like top.frameB, and know that you are referring to the right frame, regardless of what frame the expression is evaluated in. The key here is to create an absolute naming convention for frames rather than using the relative naming convention that JavaScript provides by default.
As we saw in the error handler example, JavaScript code in one window (or frame) can dynamically create an HTML document in another window (or frame). It is a lot harder for JavaScript code to dynamically create a new HTML document in its own window or frame, because doing this generally overwrites the JavaScript code itself! If your web page design calls for one static frame and two frames that have their contents dynamically updated, the static frame can contain the JavaScript code necessary to update the dynamic frames. But what if your design calls for all the frames to be dynamic? A static frame is still required, but the trick here is to create the static frame so that it is invisible! You do this by explicitly creating it at a location that is greater than 100% of the frame width or height. HTML to create such an invisible frame is shown in Example 12-5.
<!-- Create two frames that take up half the screen each, and one that --> <!-- takes up "all the rest" of the room. The third frame will be --> <!-- invisible, because it has a height of zero. --> <frameset rows="50%,50%,*"> <!-- first two frames start out empty, loading no documents --> <frame name="dynamic_frame_1"> <frame name="dynamic_frame_2"> <!-- invisible frame contains the code that will --> <!-- dynamically update the others --> <frame name="invisible_frame" src="program.html"> </frameset>
A technique related to dynamically generating frame content is the use of the TARGET attribute of <A>, <AREA>, and <FORM> tags. This attribute was discussed earlier in this chapter--it directs the browser to load the URL pointed to by a hyperlink into the named frame or window, or to load the results of form submission into the named frame. This, too, is a very useful way to change the contents of one frame from another frame.
Another HTML technique that is possible with frames in Navigator 3.0 is creating borderless frames. A borderless frame is visible to the user but its border is not. You can use borderless frames when you want an region of the screen that can display HTML content independently of the rest of the page, but which fits "seamlessly" with its neighboring frames. You can create borderless frames with attributes like those shown here. Note that the entire frameset must be borderless, since if one frame is borderless, its adjoining neighbors must be borderless, too:
<frameset border=no width=0 rows="10%,*"> <frame name="banner" src="ad.html"> <frame name="main" src="content.html"> </frameset>
We'd described how you can use JavaScript running in one frame to dynamically create HTML content for another frame. But in this discussion we have always created the frames themselves with a frameset specified in a static HTML file. Since frames are specified in HTML, there is no reason we cannot create them dynamically as well. Example 12-6 shows how it can be done. This example opens a small new window, dynamically creates four frames in it, and then, using the setTimeout() method, periodically changes the background color of each frame, creating a simple but colorful animated display, which is pictured in Figure 12-3. The Stop button in the original window stops the animation using clearTimeout() and closes the new window using the Window.close() method. This example brings together many of the window and frame programming techniques we've been discussing.
<HTML> <HEAD> <SCRIPT LANGUAGE="JavaScript1.1"> // open a new window var n = window.open('', 'f', 'width=400,height=400'); // dynamically create frames in that new window // note the use of the special about:blank URL to get empty frames n.document.write('<frameset rows="50%,50%" cols="50%,50%">'); n.document.write('<frame name="f1" src="about:blank">'); n.document.write('<frame name="f2" src="about:blank">'); n.document.write('<frame name="f3" src="about:blank">'); n.document.write('<frame name="f4" src="about:blank">'); n.document.write('</frameset>'); // an array of the colors we cycle through for the animation colors = new Array("red","green","blue","yellow","white"); // an array of the frames we cycle through (in this order) windows = new Array(n.f1, n.f2, n.f4, n.f3); // the current color and frame counters var c = 0, f = 0; // a variable that holds the current timeout id (used to cancel the timeout) var timeout = null; // This function sets the "next" frame in the list to the "next" color // in the list. We call it once to start the animation, and then it // arranges to invoke itself every quarter second after that. function change_one_frame() { // dynamically output the HTML necessary to set the background color windows[f].document.write('<BODY BGCOLOR="' + colors[c] + '">'); windows[f].document.close(); f = (f + 1) % 4; // increment frame counter c = (c + 1) % 5; // increment color counter // arrange to be called again in 250 milliseconds and // save the timeout id so that we can stop this crazy thing timeout = setTimeout("change_one_frame()", 250); } </SCRIPT> </HEAD> <!-- start the frame animation when the document is fully loaded --> <BODY onLoad="change_one_frame();"> <!-- Create a button to stop the animation with clearTimeout() --> <!-- and close the window with close(). --> <FORM> <INPUT TYPE="button" VALUE="Stop" onClick="if (timeout) clearTimeout(timeout); if (!n.closed) n.close();"> </FORM> </BODY> </HTML>