Frames are a way of splitting up the browser window into various panes, into which we can then load different HTML documents. The frames are defined in a frameset-defining page by the <frameset> and <frame> tags. The <frameset> tag is used to contain the <frame> tags and specifies how the frames should look on the page. The <frame> tags are then used to specify each frame and to include the required documents in the page.
We saw in Chapter 5 that the window object represents the browser's frame onto your page or document. If you have a page with no frames, there will be just one window object. However, if you have more than one frame, there will be one window object for each frame. Except for the very top-level window of a frameset, each window object is contained inside another.
The easiest way to demonstrate this is through an example in which we create three frames, a top frame with two frames inside it.
For this multi-frame example, we'll need to create three HTML files. The first is the frameset-defining page.
<html> <frameset rows="50%,*" ID="TopWindow"> <frame name="UpperWindow" src="UpperWindow.htm"> <frame name="LowerWindow" src="LowerWindow.htm"> </frameset> </html>
Save this as TopWindow.htm. Note that the src attributes for the two <frame> tags in this page are UpperWindow.htm and LowerWindow.htm. We will create these next.
<html> <head> <script language="JavaScript" type="text/javascript"> function window_onload() { alert("The name of the upper frame's window object is " + window.name); alert("The location of UpperWindow's parent is " + window.parent.location.href); } </script> </head> <body onload="return window_onload()"> <p>Upper Frame</p> </body> </html>
This page is the source page for the top frame with the name UpperWindow and needs to be saved as UpperWindow.htm. The final page is very similar to this:
<html> <head> <script language="JavaScript"> function window_onload() { alert("The name of the lower frame's window object is " + window.name); alert("The location of LowerWindow's parent is " + window.parent.location.href); } </script> </head> <body language=JavaScript onload="return window_onload()"> <p>Lower Frame</p> </body> </html>
This is the source page for the lower frame; save it as LowerWindow.htm.
These three pages fit together so that UpperWindow.htm and LowerWindow.htm are contained within the TopWindow.htm page.
When loaded into the browser, we have three window objects. One is the parent window object and contains the file TopWindow.htm, and two are child window objects, containing the files UpperWindow.htm and LowerWindow.htm. The two child window objects are contained within the parent window, as shown in Figure 7-1.
If any of the frames had frames contained inside them, these would have window objects that were children of the window object of that frame.
When you load TopWindow.htm into your browser, you'll see a series of four message boxes as shown in Figures 7-2 through 7-5. These are making use of the window object's properties to gain information and demonstrate the window object's place in the hierarchy.
Let's look at the frameset-defining page, starting with TopWindow.htm as shown in the following:
<html> <frameset rows="50%,*" ID=TopWindow> <frame name="UpperWindow" src="UpperWindow.htm"> <frame name="LowerWindow" src="LowerWindow.htm"> </frameset> </html>
The frameset is defined using the <frameset> tag. We use two attributes: rows and id. The rows attribute takes the value "50%,*" meaning that the first frame should take up half of the length of the window, and the second frame should take up the rest of the room. The id attribute is used to give a name that we can use to reference the page.
The two child windows are created using <frame> tags. In each of the <frame> tags, we specify a name by which the window objects will be known and the src attribute of the page that will be loaded into the newly created windows and will form the basis of the document object that each window object contains.
Let's take a look at the UpperWindow.htm file next. In the <body> tag of the page, we attach a function window_onload() to the window object's onload event handler. This event handler is called when the browser has finished loading the window, the document inside the window, and all the objects within the document. It's a very useful place to put initialization code or code that needs to change things once the page has loaded but before control passes back to the user.
<body language=JavaScript onload="return window_onload()">
This function is defined in a script block in the head of the page as follows:
function window_onload() { alert("The name of the upper frame's window object is " + window.name); alert("The location of UpperWindow's parent is " + window.parent.location.href); }
The window_onload() function makes use of two properties of the window object for the frame that the page is loaded in: its name and parent properties. The name property is self-explanatory—it's the name we defined in the frameset page. In this case, the name is UpperWindow.
The second property, the parent property, is very useful. It gives you access to the window object of the frame's parent. This means you can access all of the parent window object's properties and methods. Through these, you can access the document within the parent window as well as any other frames defined by the parent. Here, we display a message box giving details of the parent frame's file name or URL by using the href property of the location object (which itself is a property of the window object).
The code for LowerWindow.htm is identical to the UpperWindow.htm, but with different results because we are accessing a different window object. The name of the window object this time is LowerWindow. However, it shares the same parent window as UpperWindow, and so when we access the parent property of the window object, we get a reference to the same window object as we did in UpperWindow. The message box demonstrates this by displaying the file name/URL, or href property, and this matches the file name of the page displayed in the UpperWindow frame.
If you load the example in Internet Explorer and then load it into Netscape browser, you may notice a very important difference and one that highlights the often subtle ways that browsers are incompatible. In IE, the message boxes for the LowerWindow object may appear first, meaning that the onload event handler of that window has fired before the onload event handler of the UpperWindow. However, in Netscape, it's usually the other way around; the UpperWindow object's onload event handler fires first and then the LowerWindow object's event handler fires. This may not be important here, but there will be times when the order in which events fire is important and affects the working of your code. It's an incompatibility that's worth noting and watching out for in your own programs. Note that it would be very risky relying on Netscape loading in one order and IE the other—it can vary, and often for no apparent reason.
We've seen that each frame exists as a different window and gets its own window object. In addition, we saw that we can access the window object of a frameset-defining page from any of the frame pages it specifies, by using the window object's parent property. Once we have a reference to the parent window's window object, we can access its properties and methods in the same way we access the window object of the current page. In addition, we have access to all the JavaScript variables and functions defined in that page.
Let's look at a more complex example, where we use the top frame to keep track of pages as the user navigates the website. We're creating five pages in this example, but don't panic; four of the pages are almost identical. The first page that needs to be created is the frameset-defining page.
<html> <head> <title>The Unchanging frameset page</title> <script Language=JavaScript> var pagesVisited = new Array(); function returnPagesVisited() { var returnValue = "So far you have visited the following pages\n"; var pageVisitedIndex; var numberOfPagesVisited = pagesVisited.length; for (pageVisitedIndex = 0; pageVisitedIndex < numberOfPagesVisited; pageVisitedIndex++) { returnValue = returnValue + pagesVisited[pageVisitedIndex] + "\n"; } return returnValue; } function addPage(fileName) { var fileNameStart = fileName.lastIndexOf("/") + 1; fileName = fileName.substr (fileNameStart); pagesVisited[pagesVisited.length] = fileName; return true; } </script> </head> <frameset cols="50%,*"> <frame name=fraLeft src="page_a.htm"> <frame name=fraRight src="page_b.htm"> </frameset> </html>
Save this page as frameset_page.htm.
Notice that the two frames have the src attributes initialized as page_a.htm and page_b.htm. However, we also need to create page_c.htm and page_d.htm since we will be allowing the user to choose the page loaded into each frame from these four pages. We'll create the page_a.htm page first, as shown in the following:
<html> <head> <script language=JavaScript> function butShowVisited_onclick() { document.form1.txtaPagesVisited.value = window.parent.returnPagesVisited(); } </script> </head> <body onload="window.parent.addPage(window.location.href);"> <center> <font size=6 color=MidnightBlue face=verdana> This is Page A </font> </center> <p> <A href="page_a.htm">Page A</A> <A href="page_b.htm">Page B</A> <A href="page_c.htm">Page C</A> <A href="page_d.htm">Page D</A> </p> <form name=form1> <textarea rows=10 cols=35 name=txtaPagesVisited wrap=hard> </textarea> <br> <input type="button" value="List Pages Visited" name=butShowVisited onclick="butShowVisited_onclick()"> </form> </body> </html>
Save this page as page_a.htm.
The other three pages are identical to page_a.htm, except for one line, so you can just cut and paste the text from page_a.htm. Change the HTML that displays the name of the page loaded to the following:
<center>
<font size=6 color=MidnightBlue face=verdana>
This is Page B
</font>
</center>
Then save this as page_b.htm.
Do the same again, to create the third page (page C):
<center>
<font size=6 color=MidnightBlue face=verdana>
This is Page C
</font>
</center>
The final page is again a copy of page_a.htm except for the lines
<center>
<font size=6 color=MidnightBlue face=verdana>
This is Page D
</font>
</center>
Save this as page_d.htm.
Load FramesetPage.htm into your browser and navigate to various pages by clicking the links. Then click the List Pages Visited button on Page A in the left-hand frame, and you should see a screen similar to the one shown in Figure 7-6.
Click the links in either frame to navigate the page to a new location. For example, click the Page C link in the right frame, then the Page D link in the left frame. Click the left frame's List Pages Visited button and you'll see that page_c.htm and page_d.htm have been added to the list.
Normally when a new page is loaded, any variables and their values in the previous page are lost, but with framesets it does not matter which page is loaded into each frame—the top frame remains loaded and its variables keep their values. What we are seeing in this example is that, regardless of which page is loaded in each frame, some global variable in the top frame is keeping track of the pages that have been viewed and the top frame's variables and functions can be accessed by any page loaded into either the left or right frames.
Let's first look at the JavaScript in frameset_page.htm, which is the frameset-defining page. The head of the page contains a script block. The first thing we do in this script block is to declare the variable pagesVisited and set it to reference a new Array object. In the array, we'll be storing the file name of each page visited as the user navigates the site.
var pagesVisited = new Array();
We then have two functions. The first of the two functions, returnPagesVisited(), does what its name suggests—it returns a string containing a message and a list of each of the pages visited. It does this by looping through the pagesVisited array, building up the message string inside the variable returnValue, which is then returned to the calling function.
function returnPagesVisited() { var returnValue = "So far you have visited the following pages\n"; var pageVisitedIndex; var numberOfPagesVisited = pagesVisited.length; for (pageVisitedIndex = 0; pageVisitedIndex < numberOfPagesVisited; pageVisitedIndex++) { returnValue = returnValue + pagesVisited[pageVisitedIndex] + "\n"; } return returnValue; }
The second function, addPage(), adds the name of a page to the pagesVisited array.
function addPage(fileName) { var fileNameStart = fileName.lastIndexOf("/") + 1; fileName = fileName.substr(fileNameStart); pagesVisited[pagesVisited.length] = fileName; return true; }
The fileName parameter passed to this function is the full file name and path of the visited page, so we need to strip out the path to leave us with just the file name. The format of the string will be something like file:///D:/myDirectory/page_b.htm, and we need just the bit after the last / character. So in the first line of code, we find the position of that character and add one to it because we want to start at the next character.
Then using the substr() method of the String object in the following line, we extract everything from character position fileNameStart right up to the end of the string. Remember that the substr() method takes two parameters, namely the starting character we want and the length of the string we want to extract, but if the second parameter is missing, all characters from the start position to the end are extracted.
We then add the file name into the array, the length property of the array providing the next free index position.
We'll now turn to look collectively at the frame pages, namely page_a.htm, page_b.htm, page_c.htm, and page_d.htm. In each of these pages, we create a form called form1.
<form name=form1> <textarea rows=10 cols=35 name=txtaPagesVisited wrap=hard> </textarea> <br> <input type="button" value="List Pages Visited" name=butShowVisited onclick="butShowVisited_onclick()"> </form>
This contains the textarea control that will display the list of pages visited, and a button the user can click to populate the textarea.
When one of these pages is loaded, its name is put into the pagesVisited array defined in frameset_page.htm by connecting the window object's onload event handler to the addPage() function that we also created in frameset_page.htm. We connect the code to the event handler in the <body> tag of the page as follows:
<body onload="window.parent.addPage(window.location.href);">
Recall that all the functions we declare in a page are contained, like everything else in a page, inside the window object for that page, but because the window object is the global object, we don't need to prefix the name of our variables or functions with window.
However, this time the function is not in the current page, but in the frameset_page.htm page. The window containing this page is the parent window to the window containing the current page. We need, therefore, to refer to the parent frame's window object using the window object's parent property. The code window.parent gives us a reference to the window object of frameset_page.htm. With this reference, we can now access the variables and functions contained in frameset_page.htm. Having stated which window object we are referencing, we just add the name of the function we are calling, in this instance the addPage() function. We pass this function the location.href string, which contains the full path and file name of the page, as the value for its one parameter.
As we saw earlier, the button on the page has its onclick event handler connected to a function called butShowVisited_onclick(). This is defined in the head of the page.
function butShowVisited_onclick() { document.form1.txtaPagesVisited.value = window.parent.returnPagesVisited(); }
In this function we call the parent window object's returnPagesVisited() function, which, as we saw earlier, returns a string containing a list of pages visited. The value property of the textarea object is set to this text.
That completes our look at the code in the frame pages, and as you can see, there's not much of it because we have placed all the general functions in the frameset page. Not only does this code reuse make for less typing, but it also means that all your functions are in one place. If there is a bug in a function, fixing the bug for one page also fixes it for all pages that use the function. Of course, it only makes sense to put general functions in one place; functions that are specific to a page and are never used again outside it are best kept in that page.
We've just seen how a child window can access its parent window's variables and functions, but how can frames inside a frameset access each other?
We saw a simple example earlier in this chapter, so this time let's look at a much more complex example. Once created, our page will look like that shown in Figure 7-7.
A diagram of the frame layout is shown in Figure 7-8. The text labels indicate the names that each frame has been given in the <frameset> and <frame> tags, with the exception of the top frame, which is simply the window at the top of the frameset hierarchy.
The easiest way to think of the hierarchy of such a frames-based web page is similar to how you would think of family relationships, which can be shown in a family tree diagram. If we represent our frameset like that, it looks something like Figure 7-9.
From the diagram we can see that fraBottom, the right-hand frame's bottom frame, has a parent frame called fraMain, which itself has a parent, the top window. Therefore, if we wanted to access a function in the top window from the fraBottom window, we would need to access fraBottom's parent's parent's window object. We know that the window object has the parent property, which is a reference to the parent window of that window object. So, let's use that and create the code to access a function, for example, called myFunction(), in the top window.
window.parent.parent.myFunction();
Let's break this down. First
window.parent
gets us a reference to the parent window object of the window in which the code is running. The code is in fraBottom, so window.parent will be fraMain. However, we want the top window, which is fraMain's parent, so we add to the preceding code to make
window.parent.parent
Now we have a reference to the top window. Finally, we call myFunction() by adding that to the end of the expression.
window.parent.parent.myFunction();
What if we wanted to access the window object of fraMenu from code in fraBottom? Well, we have most of the code we need already. We saw that window.parent.parent gives us the top window, so now we just want that window's child window object called fraMenu. We can do this in three ways, all with identical results.
We can use its index in the frames[] array property of the window object as follows:
window.parent.parent.frames[0]
Alternatively, we can use its name in the frames[] array like this:
window.parent.parent.frames["fraMenu"]
Finally, we can reference it directly by using its name as we can with any window object.
window.parent.parent.fraMenu
The third method is the easiest unless you have a situation where you don't know the name of a frame and need to access it by its index value in the frames[] array, or perhaps where you are looping through each child frame in turn.
Since window.parent.parent.fraMenu gets us a reference to the window object associated with fraMenu, to access a function myFunction() or variable myVariable, we would just type
window.parent.parent.fraMenu.myFunction
or
window.parent.parent.fraMenu.myVariable
What if we want to access not a function or variable in a page within a frame, but a control on a form or even the links on that page? Well, let's imagine we want to access a control named myControl, on a form called myForm in the fraMenu page from the fraBottom page.
We found that window.parent.parent.fraMenu gives us the reference to fraMenu's window object from fraBottom, but how do we reference a form there?
Basically, it's the same as how we access a form from the inside of the same page as the script, except that we need to reference not the window object of that page but the window object of fraMenu, the page we're interested in.
Normally, we write document.myForm.myControl.value, with window being assumed since it is the global object. Strictly speaking, it's window.document.myForm.myControl.value.
Now that we're accessing another window, we just reference the window we want and then use the same code. So we need
window.parent.parent.fraMenu.document.myForm.myControl.value
if we want to access the value property of myControl from fraBottom. As you can see, references to other frames can get pretty long, and in this situation it's a very good idea to store the reference in a variable. For example, if we are accessing myForm a number of times, we could write
var myFormRef = window.parent.parent.fraMenu.document.myForm;
so that now we can write
myFormRef.myControl.value;
rather than
window.parent.parent.fraMenu.document.myForm.myControl.value;
Using the parent property can get a little tedious when you want to access the very top window from a frame quite low down in the hierarchy of frames and window objects. An alternative is the window object's top property. This returns a reference to the window object of the very top window in a frame hierarchy. In our example, this is top window.
For instance, in the example we just saw
window.parent.parent.fraMenu.document.myForm.myControl.value;
could be written as
window.top.fraMenu.document.myForm.myControl.value;
Although, because the window is a global object, we could shorten that to just
top.fraMenu.document.myForm.myControl.value;
So when should you use top rather than parent, or vice versa?
Both properties have advantages and disadvantages. The parent property allows you to specify window objects relative to the current window. The window above this window is window.parent, its parent is window.parent.parent, and so on. The top property is much more generic; top is always the very top window regardless of the frameset layout being used. There will always be a top, but there's not necessarily going to always be a parent.parent. If you put all your global functions and variables that you want accessible from any page in the frameset in the very top window, window.top will always be valid regardless of changes to framesets beneath it, whereas using the parent property is dependent on the frameset structure above it. However, if someone else loads your website inside a frameset page of their own, then suddenly the top window is not yours but theirs, and window.top is no longer valid. You can't win, or can you?
One trick is to check to see whether the top window contains your page; if not, reload the top page again and specify that your top page is the one to be loaded. For example, check to see that the file name of the top page actually matches the name you expect. The window.top.location.href will give us the name and path—if they don't match what we want, use window.top.location.replace("myPagename.htm") to load the correct top page. However, as we'll see later, this will cause problems if someone else is loading our page into a frameset she has created—this is where something called the "same origin policy" applies. More on this later in the chapter.
Let's put all we've learned about frames and scripting between them into an example based on the frameset we've been looking at. We're going to be reusing a lot of the pages and code from the previous example in this chapter.
The first page we're creating is the top window page.
<html> <head> <title>The complex frameset page</title> <script language=JavaScript> var pagesVisited = new Array(); function returnPagesVisited() { var returnValue = "So far you have visited the following pages\n"; var pageVisitedIndex; var numberOfPagesVisited = pagesVisited.length; for (pageVisitedIndex = 0; pageVisitedIndex < numberOfPagesVisited; pageVisitedIndex++) { returnValue = returnValue + pagesVisited[pageVisitedIndex] + "\n"; } return returnValue; } function addPage(fileName) { var fileNameStart = fileName.lastIndexOf("/") + 1; fileName = fileName.substr(fileNameStart); pagesVisited[pagesVisited.length] = fileName; return true; } </script> </head> <frameset cols="200,*"> <frame name=fraMenu src="menu_page.htm"> <frame name=fraMain src="main_page.htm"> </frameset> </html>
As you can see, we've reused a lot of the code from frameset_page.htm, so you can cut and paste the script block from there. Only the different code lines are highlighted. Save this page as complex_frameset_page.htm.
Next, create the page that will be loaded into fraMenu, namely menu_page.htm.
<html> <head> <script language=JavaScript> function choosePage_onchange() { var choosePage = document.form1.choosePage; var windowobject; if (document.form1.radFrame[0].checked == true) { windowobject = window.parent.fraMain.fraTop; } else { windowobject = window.parent.fraMain.fraBottom; } windowobject.location.href = choosePage.options[choosePage.selectedIndex].value; return true; } </script> </head> <body> <form name=form1> Select frame<br> Top <input name="radFrame" checked type=radio> Bottom <input name="radFrame" type=radio> <br><br> <select name=choosePage language=JavaScript onchange="choosePage_onchange()"> <option value=page_a.htm>Page A <option value=page_b.htm>Page B <option value=page_c.htm>Page C <option value=page_d.htm>Page D </select> </form> </body> </html>
Save this as menu_page.htm.
The fraMain frame contains a page that is simply a frameset for the fraTop and fraBottom pages.
<html> <frameset rows="50%,*"> <frame name=fraTop src="page_a.htm"> <frame name=fraBottom src="page_b.htm"> </frameset> </html>
For the next four pages, we reuse the four pages, page_a.htm, page_b.htm, page_c.htm, and page_d.htm, from the first example. You'll need to make a few changes, as shown in the following code. Again, all the pages are identical except for the text shown in the page, so only page_a.htm is shown. Amend the rest in a similar way.
<html> <head> <script language=JavaScript> function butShowVisited_onclick() { document.form1.txtaPagesVisited.value = window.top.returnPagesVisited(); } function setFrameAndPageControls(linkIndex) { var formobject = window.parent.parent.fraMenu.document.form1; formobject.choosePage.selectedIndex = linkIndex; if (window.parent.fraTop == window.self) { formobject.radFrame[0].checked = true; } else { formobject.radFrame[1].checked = true; } return true; } </script> </head> <body language=JavaScript onload="return window.top.addPage(window.location.href);"> <center> <font size=6 color=MidnightBlue face=verdana> This is Page A </font> </center> <p> <A href="page_a.htm" name="pageALink" onclick="return setFrameAndPageControls(0)">Page A</A> <A href="page_b.htm" name="pageBLink" onclick="return setFrameAndPageControls(1)">Page B</A> <A href="page_c.htm" name="pageCLink" onclick="return setFrameAndPageControls(2)">Page C</A> <A href="page_d.htm" name="pageDLink" onclick="return setFrameAndPageControls(3)">Page D</A> </p> <form name=form1> <textarea rows=8 cols=35 name=txtaPagesVisited> </textarea> <br> <input type="button" value="List Pages Visited" name=butShowVisited onclick="butShowVisited_onclick()"> </form> </body> </html>
Resave the pages under their old names.
Load complex_frameset_page.htm into your browser, and you'll see a screen similar to that shown in Figure 7-7.
The radio buttons allow the user to determine which frame he wants to navigate to a new page. By changing the currently selected page in the drop-down list, the selected page is loaded into the frame selected by the radio buttons.
If you navigate using the links in the pages inside the fraTop and fraBottom frames, you'll notice that the selected frame radio buttons and the drop-down list in fraMenu on the left will be automatically updated to the page and frame just navigated to. Note that as the example stands, if the user loads page_a.htm into a frame, the select list doesn't allow it to load the same page in the other frame. We could improve on this example by adding a button that loads the currently selected page into the chosen frame.
The List Pages Visited buttons display a list of visited pages, as they did in the previous example.
We've already seen how the code in the complex_frameset_page.htm defining the top window works, as it is very similar to our previous example. However, we'll just look quickly at the <frameset> tags where, as you can see, the names of the windows are defined in the names of the <frame> tags.
<frameset cols="200,*"> <frame name=fraMenu src="menu_page.htm"> <frame name=fraMain src="main_page.htm"> </frameset>
Notice also that the cols attribute of the <frameset> tag is set to "200,*". This means that the first frame will occupy a column 200 pixels wide, and the other frame will occupy a column taking up the remaining space.
Let's look in more detail at the fraMenu frame containing menu_page.htm. At the top of the page, we have our main script block. This contains the function choosePage_onchange(), which is connected to the onchange event handler of the select box lower down on the page. The select box has options containing the various page URLs.
The function starts by defining two variables. One of these, choosePage, is a shortcut reference to the choosePage Select object further down the page.
function choosePage_onchange() { var choosePage = document.form1.choosePage; var windowobject;
The if...else statement then sets our variable windowobject to reference the window object of whichever frame the user has chosen in the radFrame radio button group.
if (document.form1.radFrame[0].checked == true) { windowobject = window.parent.fraMain.fraTop; } else { windowobject = window.parent.fraMain.fraBottom; }
As we saw earlier, it's just a matter of following through the references, so window.parent gets us a reference to the parent window object. In this case, window.top would have done the same thing. Then window.parent.fraMain gets us a reference to the window object of the fraMain frame. Finally, depending on which frame we want to navigate in, we reference the fraTop or fraBottom window objects contained within fraMain, using window.parent.fraMain.fraTop or window.parent.fraMain.fraBottom.
Now that we have a reference to the window object of the frame in which we want to navigate, we can go ahead and change its location.href property to the value of the selected drop-down list item, causing the frame to load that page.
windowobject.location.href = choosePage.options[choosePage.selectedIndex].value; return true; }
As we saw before, main_page.htm is simply a frameset-defining page for fraTop and fraBottom. Let's now look at the pages we're actually loading into fraTop and fraBottom. Because they are all the same, we'll just look at page_a.htm.
Let's start by looking at the top script block. This contains two functions, butShowVisited_onclick() and setFrameAndPageControls(). We saw the function butShowVisited_onclick() in the previous example.
function butShowVisited_onclick() { document.form1.txtaPagesVisited.value = window.top.returnPagesVisited(); }
However, because the frameset layout has changed, we do need to change the code. Whereas previously the returnPagesVisited() function was in the parent window, it's now moved to the top window. As you can see, all we need to do is change the reference from window.parent.returnPagesVisited(); to window.top.returnPagesVisited();.
As it happens, in the previous example the parent window was also the top window, so if we had written our code in this way in the first place, there would have been no need for changes here. It's often quite a good idea to keep all your general functions in the top frameset page. That way all your references can be window.top, even if the frameset layout is later changed.
The new function in this page is setFrameAndPageControls(), which is connected to the onclick event handler of the links defined lower down on the page. This function's purpose is to make sure that if the user navigates to a different page using the links rather than the controls in the fraMenu window, those controls will be updated to reflect what the user has done.
The first thing we do is set the formobject variable to reference the form1 in the fraMenu page as follows:
function setFrameAndPageControls(linkIndex) { var formobject = window.parent.parent.fraMenu.document.form1;
Let's break this down.
window.parent
gets us a reference to fraMain's window object. Moving up the hierarchy, we use
window.parent.parent
to get a reference to the window object of the top window. Yes, you're right. We could have used window.top instead, and this would have been a better way to do it. I'm doing it the long way here just to demonstrate how the hierarchy works.
Now we move down the hierarchy, but on the other side of our tree diagram, to reference the fraMenu's window object.
window.parent.parent.fraMenu
Finally, we are only interested in the form and its controls, so we reference that object like this:
window.parent.parent.fraMenu.document.form1
Now that we have a reference to the form, we can use it just as we would if this were code in fraMenu itself.
The function's parameter linkIndex tells us which of the four links was clicked, and we use this value in the next line of the function's code to set which of the options is selected in the drop-down list box on fraMenu's form.
formobject.choosePage.selectedIndex = linkIndex;
The if...else statement is where we set the fraMenu's radio button group radFrame to the frame the user just clicked on, but how can we tell which frame this is?
if (window.parent.fraTop == window.self) { formobject.radFrame[0].checked = true } else { formobject.radFrame[1].checked = true }
We check to see whether the current window object is the same as the window object for fraTop. We do this using the self property of the window object, which returns a reference to the current window object, and window.parent.fraTop, which returns a reference to fraTop's window object. If one is equal to the other, we know that they are the same thing and that the current window is fraTop. If that's the case, the radFrame radio group in the fraMenu frame has its first radio button checked. Otherwise, we check the other radio button for fraBottom.
The last thing we do in the function is return true. Remember that this function is connected to an A object, so returning false cancels the link's action, and true allows it to continue, which is what we want.
return true; }