Sharing Data Between Web Page Frames Using JavaScript

Frames are a powerful addition to HTML. When used in conjunction with JavaScript, you can create impressive pages that previously required knowledge of complex, server-based CGI.


May 01, 1996
URL:http://www.drdobbs.com/web-development/sharing-data-between-web-page-frames-usi/184409884

Figure 1

Figure 1

Figure 3

Figure 4

Figure 3

Figure 4

MAY96: Sharing Data Between Web Page Frames Using JavaScript

Sharing Data Between Web Page Frames Using JavaScript

Implementing a hidden-frame technique

Tom Tessier

Tom is a student in the engineering physics department at the University of Alberta, Canada. He can be reached at [email protected].


Although HotJava and client-side image maps are significant features in Netscape Navigator 2.0, the addition of frames and JavaScript may make a more immediate impact on average Web users. With these, the very nature of the Web will be changed from static, boring, non-interactive pages to sites that are different each time users visit them, containing table of contents information displayed at all times, interactive games, and more.

In this article, I'll examine how to create such dynamic pages.

The <Frameset> Tag

Frames divide a Web page into multiple scrollable regions, allowing different HTML documents to be displayed in a single window. Frames can be targeted by other URLs on the same page, share data, display table of contents and copyright information at all times, and so on.

The HTML code that initiates a frame-based page is actually quite small, its only purpose being to divide the page up into regions and load documents into these different areas. Two distinct tags are required:

A COLS="parameter" attribute placed within the <Frameset> tag divides the page vertically, while a ROWS="parameter" command splits the page horizontally. For example, to divide the page into two equal halves--one frame on the left and one on the right--you would enter the tag <Frameset COLS="50%,50%">. To implement both column and row frames, simply nest the <frameset> tags. The placement of the nested frames is important, since the frame order is based on the sequence in which the tags are positioned in the HTML; see
Example 1 and the resulting page in Figure 1.

Frames, by default, are empty and non-functional. You must point each frame to a document using the identifier <FRAME SRC="URL">. The physical frame referenced by the SRC command depends on the location of the current <Frame> tag. For example, if the second FRAME SRC statement within a <Frameset> points to THISURL, then THISURL will be loaded into the second frame; see Example 1 and Figure 1. For a complete list of attributes available for both the <Frameset> and <Frame> tags, see Figure 2. Note that a frame HTML document has no BODY, and no tags normally associated with the <Body> identifier may appear before the <Frameset>.

Referencing Frames

Frames can be referenced using the TARGET command. By placing TARGET= "window_name" into the anchor, client-side image map, or form tags, the URL pointed to by the tag will be loaded into the frame called window_name; see Example 2.

Another important JavaScript document object useful to frames is location.href, which lets you dynamically alter the location of the current window or frame. A particularly powerful use of this object is to make the frame point to itself, allowing the JavaScript code to generate a different page at various times. This is done by simply entering location.href=location.href. When encountered, this causes the page to reload and reexecute. The problem is, all of the data in the page will be reset every time the URL reloads. Obviously, if you plan to keep track of variables between frame sets, a different scheme must be used.

The key is to store the page in a frame. Then, using an undocumented feature of JavaScript, a dynamically loadable page may be created. The undocumented object, parent.variable_name, allows a child frame to freely access the parent's JavaScript data, as illustrated in Example 3 (the page is displayed in Figure 3). Notice that at least two frames must be set up before Netscape will initiate the frame session. The second frame is sized using the parent frame to hold all of the child's variables; the child may reload at will with no risk of the parent losing its data.

You also can use the parent frame to store actual HTML code. This is useful in creating interactive pages by writing the variable's contents to the page at the start of the document (writing to the page after a document has loaded is illegal). For example, insert the line document.write (parent.code); see Example 3. It is even possible to have code change with time by simply writing new text into the parent.code variable. Conceivably, one could use this technique to store all of the required HTML documents for a page into a single file.

It should be noted that Netscape Navigator 2.0, although now in release, is still buggy and can have problems with frame-data accessing. For example, resizing the page will cause the entire document to reset. Keep this in mind when using frame-data sharing.

Creating an Interactive Questionnaire

In my article, "Using JavaScript to Create Interactive Web Pages" (DDJ, March 1996), I presented JavaScript code that generated a simple multiple-choice page: It prompted the user to select an option and displayed the results via form inputs. Listings One and Two are that same multiple-choice code converted to the dynamic-frame method. As can be seen in Figure 4, the resulting page is much more attractive, looking more like a CGI-generated document than a cheap form hack. The advantages of using the dynamic-frame method, as opposed to the form-output technique are many. The resulting Web pages are not restricted solely to forms for output: Pictures, fonts, colors, and anything else that would ordinarily be placed in HTML may be used.

Conclusion

Frames are a powerful addition to HTML. By simply adding frames to a Web page, the documents contained there can be much easier to use. Frame-based table of contents information, form results, and so on, increase the quality of the World Wide Web. Used in conjunction with JavaScript, filenames allow the creation of impressive pages previously realizable only through complex server-based CGI.

Figure 1: The Web page resulting from the HTML code in Example 1.

Figure 2: Summary of the frame tags and their attributes: (a) <Frameset> tag; (b) <Frame> tag; (c) <Noframe> tag.

(a)

<FRAMESET parameter> frame data </FRAMESET>. Divide a page into frames, where frame data holds the <FRAME> and nested <FRAMESET> tags, while parameter is one of the following:

To have both columns and rows on a page, the <FRAMESET> tags must be nested (see Example 1). The height and width value lists follow the same syntax: a comma separated list, with the total number of items indicating the desired number of row or column frames. The format of each item in the list is as follows:

Integer Value. Indicates the size, in pixels, of the frame (if not used in conjunction with the values below, NetScape will frequently override this value to force the frame to fill up the entire window).

Percentage Value. Indicates the percentage size of the frame (if the total percentage does not equal 100 percent, NetScape will force the frames to fill up the entire window). For example: <FRAMESET COLS="50%,50%"> divides the page into two equally sized column frames, one on the left and one on the right.

Relative Value. Implied with the *, relative values specify the size of each frame in relation to the remaining items in the list. For example:

In general, it is easiest to use percentages. Mixing of the three types of values is also allowed. For example:

(b) <FRAME parameters>. Define the document attributes of each frame, where the parameters are:

(c) <NOFRAMES> Alternate HTML code </NOFRAMES>. The NOFRAME tag provides alternative content viewable by non-Frame-capable clients. NetScape Navigator 2.0 will ignore all tags and data contained between the start and end NOFRAME tags.

Figure 3: The page resulting from Example 3.

Figure 4: Typical page using JavaScript for frame data sharing (dynamic-frame method).

Example 1: Sample HTML file illustrating how to divide a page into frames.

<HTML>
<!-- COLS divide the page vertically; ROWS divide the page
horizontally -->
<FRAMESET COLS="50%,50%"> <!-- divide page vertically down the
middle - creating two equal halves: one on the left, the other on the
right -->
  <FRAMESET ROWS="40%,20%,40%"> <!-- divide the left half horizontally
into three pieces: one on top, a smaller one in the middle, and one on
the bottom -->
    <FRAME SRC="frame1.html"> <!-- set the top 40% pointing to
frame1.html -->
    <FRAME SRC="frame2.html"> <!-- point the middle 20% to URL
frame2.html -->
    <FRAME SRC="frame3.html"> <!-- set the bottom 40% to
frame3.html -->
  </FRAMESET>
  <FRAMESET ROWS="*,*"> <!-- divide the right half horizontally
into two equal pieces: top and bottom. Could have used "50%,50%"
instead. -->
    <FRAME SRC="frame4.html"> <!-- set the top half to
frame4.html -->
    <FRAME SRC="frame5.html"> <!-- set the bottom half to
frame5.html -->
  </FRAMESET>
</FRAMESET>
</HTML>

Example 2: Sample HTML code showing how to use the TARGET command to load URLs into another frame. (a) index.html contents; (b) text1.html contents; (c) init.html contents.

(a)
<HTML>
<FRAMESET COLS="35%,*">
    <FRAME SRC="text1.html">
    <FRAME SRC="init.html" NAME="main_frame">
</FRAMESET>
</HTML>

(b)
<HTML>
<BODY>
Table of Contents:<BR>
<!-- by default, target all links to frame "main_frame" -->
<BASE TARGET="main_frame">
<A HREF="learn.html">1. Learning Opportunities</A><BR>
<A HREF="codes.html">2. Activation Codes</A><BR>
<A HREF="hiring.html">3. End of Year Hiring Positions</A><BR>
<A HREF="firing.html">4. End of Year Firing Positions</A><BR>
<!-- place a clickable client side image map here now -->
<IMG SRC="navimage.gif" ALIGN="center"  USEMAP="#navbar">
<MAP NAME="navbar">
<!-- target the client side image map link to the main_frame -->
<AREA SHAPE="RECT" COORDS="0,0,19,19" HREF="copyright.html"
TARGET="main_frame">
<AREA SHAPE="RECT" COORDS="20,0,39,19" HREF="logo.html"
TARGET="main_frame">
</MAP>
<!-- target the form results to the frame "main_frame" -->
<FORM METHOD="GET" ACTION="cgi-bin/process.cgi"
TARGET="main_frame">
Please enter the job you are planning on applying for:<BR>
<INPUT TYPE="text" SIZE=50 NAME="JOBTITLE"><P>
<INPUT TYPE="submit" VALUE="Send">
<INPUT TYPE="reset" VALUE="Clear">
</FORM>
</BODY>
</HTML>

(c)
<HTML>
<BODY>
<H1 ALIGN="center">Joe's Pizza Corp. Job Information</H1>
</BODY>
</HTML>

Example 3: Sample HTML files illustrating dynamic frame reloading and frame data sharing using JavaScript; (a) index.html contents; (b) main.html contents.

(a)
<HTML>
<SCRIPT LANGUAGE="LiveScript">
// variables defined here may be accessed by any of the frames defined
// below via parent.variable_name command. NOTE: if NetScape window
// is resized, the variables will be reset to their initial values.
var count=0
var code="<BR>HTML code stored in Parent frame."
code=code+"<BR>Standard <H1>HTML</H1> may be used."
</SCRIPT>
<!-- must define two frames... although the second frame is not used, a
minimum of two frames is needed before netscape will initiate a frame
setup -->
<FRAMESET ROWS="*,0">
    <FRAME SRC="main.html">
    <FRAME SCROLLING="no" NORESIZE>
</FRAMESET>
</HTML>

(b)
<SCRIPT LANGUAGE="LiveScript">
<!-- hide this script tag's contents from old browsers
function refresh()
{
location.href=location.href // reload this frame
}
parent.count++  // increment variable which counts the number of
// times the page has been loaded
document.write ('<FORM><LI><INPUT TYPE="radio" onClick="refresh()">')
document.write ("The page has been loaded "+parent.count+" times.")  
document.write ("<BR>"+parent.code) // write the Parent frame HTML
// code stored in the code variable
<!-- done hiding from old browsers -->
</SCRIPT>

Listing One

<!-- contents of index.html file -->
<HTML>
<SCRIPT LANGUAGE="LiveScript">
<!-- hide from old netscape browsers -->
// variables defined here may be accessed by any of the frames defined
// below via the parent.variable_name command. NOTE: if the NetScape window
// is resized, the variables will be reset to their initial values.
// ie: these variables are available to the child frames.
// variables
var totalnum=3 // total # of questions
var correctans=0 // the question # (from 0 to N-1) of the correct
// answer to each question. Set in Listing2 (scratcha.html)
var count=0 // counter variable. Initialized to zero
var arraycount=1 // index into the questans array. Intialized to 1
// since that is where the first array string begins.
var totalright=0 // total # of questions answered right (0 initially)
var correcttext="blank" // the text of the correct answer
<!-- done hiding from old netscape browsers -->
</SCRIPT>
<!-- must define two frames... although the second frame is not used, a
minimum of two frames is needed before netscape will initiate a frame
setup -->
<FRAMESET ROWS="*,0"> <!-- give the 2nd frame 0 pixels, and assign 
the rest of the space to frame 1 via the "*" -->
    <FRAME SRC="scratcha.html">
    <FRAME SCROLLING="no" NORESIZE>
</FRAMESET>
</HTML>

Listing Two

<!-- contents of scratcha.html file -->
<SCRIPT LANGUAGE="LiveScript">
<!-- hide this script tag's contents from old browsers
// editable local frame variables
        var totalans = 3 // total # of answers per question (used to
// generate the question/answer array below)
// This function defines an array such that the first property, length, (with
// index of zero), represents the number of elements in array. The remaining 
// properties have an integer index of 1 or greater, and are initialized to 0.
function MakeArray(n) {
   this.length = n;
   for (var i = 1; i <= n; i++) {
     this[i] = 0 }
     return this
     }
function checkout(questionnum)
{
 if ( parent.count > parent.totalnum ) // if user 
// clicks a radio button after the test is complete, display an alert
  {
 alert('To return to the main page, click on "Return to Main Page."')
  }
 else
  {
    if ( questionnum == parent.correctans) // if the currently
// selected question is the correct response, say so
        {
// dunno if allowed to do below line
                parent.totalright++
                alert("Correct.")
        }
     else // if wrong answer, display the correct one
      {
       alert("Incorrect. The answer is:\n"+parent.correcttext)
      }
   parent.count++ // increment count so can goto next question
   if ( parent.count == parent.totalnum) // if count =
// the total # of questions
      {
        parent.count++ // then increment count again so is 
// greater than totalnum so can activate the alert above if user
// tries to click on a radio button after the test is complete
      }
  }
location.href=location.href // reload the page (will reload the page
// into which this function is placed) so can update using the new
// values stored in the parent frame's variables
}
// formula for # of array elements:
// total number of questions + 1 (have to include the Test is 
// Complete question at the end of the question data) times total
// answers allowed per question + 2 (plus two because have to have a
// string for the actual question itself and for the value indicating
// the correct answer). eval used to convert (totalnum)*(totalans+2) 
// into a number usable to pass to MakeArray... need eval since the 
// expression is inside a function call (ie: inside the MakeArray call)
questans = new MakeArray(eval((parent.totalnum+1)*(totalans+2)));
// array for question list (start with question #2 - define question
// one in the document's html code below)
questans[1] = "When our star (Sol) dies it will most"+" likely become:"
questans[2] = "A black hole."
questans[3] = "A white dwarf."
questans[4] = "A neutron star."
questans[5] = 1 // correct answer here is "A white dwarf" (list
// answers from 0 to ans#-1)
questans[6] = "Pyconuclear reactions are:"
questans[7] = "reactions which require high density."
questans[8] = "reactions which depend on heat."
questans[9] = "reactions which require low density."
questans[10] = 0 // correct answer here is "reactions which require
// high density" (list answers from 0 to ans#-1)
questans[11] = "A white dwarf maintains its compact shape via:"

questans[12] = "coulombic repulsion."
questans[13] = "fermi neutron pressure."
questans[14] = "fermi electron pressure."
questans[15] = 2 // correct answer: "fermi electron pressure."
// end of editable questions. Don't change the below (just the text
// displayed at the end of the test)
questans[16] = "The test is complete. Thank you."
questans[17] = ""
questans[18] = ""
questans[19] = ""
questans[20] = 255 // correct answer: none
// NOTE: will also need some variables in hiddenfr.html stored into
// separate forms so that can save data between URL reloads (ie: when
// location.href is set the variables will be destroyed if not stored
// in the new URL... easier to code if just place data in the hidden frame)
if ( parent.count > parent.totalnum ) // if user 
// completed all questions, display the Finished page.
 {
   document.write("<H1>The Test is Complete. You got "+
parent.totalright+" correct out of "+parent.totalnum+
" questions.</H1>")
 }
else
 {
// make arrayind = to the parent frame variable arraycount
arrayind=parent.arraycount
// write the form tag
document.write ('<FORM METHOD="post">')
// increment arrayind after each use below
// and place a <BR> tag after each to seperate each text item.
// write the question
document.write ("<H1>",questans[arrayind++],"</H1>")
// write radio button for answer 1
document.write ('<LI><INPUT TYPE="radio" onClick="checkout(0)">')
// and check if the correct answer for this question is answer 1
if ( questans[arrayind+3] == 0 )
// and if so, set the rightans variable to the text of the correct answer
   { parent.correcttext = questans[arrayind] }
// write answer 1
document.write (questans[arrayind++],"<BR>")
// write radio button for answer 2
document.write ('<LI><INPUT TYPE="radio" onClick="checkout(1)">')
// and check if the correct answer for this question is answer 2
if ( questans[arrayind+2] == 1 )
// and if so, set the rightans variable to the text of the correct answer
   { parent.correcttext = questans[arrayind] }
// write answer 2
document.write (questans[arrayind++],"<BR>")
// write radio button for answer 3
document.write ('<LI><INPUT TYPE="radio" onClick="checkout(2)">')
// and check if the correct answer for this question is answer 3
if ( questans[arrayind+1] == 2 )
// and if so, set the rightans variable to the text of the correct answer
   { parent.correcttext = questans[arrayind] }
// write answer 3
document.write (questans[arrayind++],"<BR>")
// write the closing form tag
document.write ("</FORM>")
parent.correctans = questans[arrayind++] // and set the new
// correct answer
// and save the new arrayind value into the parent frame variable
parent.arraycount=arrayind
// skip two lines
document.write ("<BR><BR>")
// now display the number of questions the user has chosen correctly so far
document.write ("You have completed "+parent.count+" of "+
parent.totalnum+" questions, with "+parent.totalright)
    if ( parent.totalright == "1" ) // if only one right, make
// sure use the word "answer" instead of the plural form "answers" 
     {
       document.write (" correct answer.")
     }
    else
     {
       document.write (" correct answers.")
     }
 }
// skip two lines
document.write ("<BR><BR>")
// link to main page:
document.write ('<LI><A HREF="index.html">Return to main page.</A>')
<!-- done hiding from old browsers -->
</SCRIPT>

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.