Manipulating Frames with VBScript and ASP
The first two examples in this chapter showed some things that can be done easily with client-side scripting. However, they didn't actually incorporate any of the features of ASP, we only suggested ways that this could be done. In this and the next examples, we'll be using ASP in conjunction with script code on the client.
While ASP is great at building the individual pages for a web application dynamically, its features are rather limited when it comes to handling the frameset pages that are so common to today's sites. Using the
Redirect method of the
Response object we can redirect to a single page, but we can't target it to a specific frame—only the browser can do that. The combination of client and server code in this example shows how scripting languages on both sides of the Web connection can, together, manage framesets and the pages inside the frames.
The Frames Example
This is more complicated than the first two examples we looked at. Before we launch into an explanation of what this example does, you might like to bring up
frameset.asp in your browser and see for yourself. Experiment by clicking on a few of the links in the left hand pane. We'll spend some time discussing the code that makes this sample work, and you'll understand it better if we're familiar with the samples behavior in the browser. Since this is now an ASP file, you also need to be sure that it comes from your server and is loaded via HTTP.
You'll notice that clicking on a page link on the left hand side loads that page into the right hand frame, and also updates the left frame itself so that only links to the pages not currently shown are available. This makes sense—if we're already viewing the second page we shouldn't be able to jump to it again.
Since we're viewing page two in the right hand frame, it's not visible in the list in the left hand frame.
Now click over to the third page, and you'll see a list of options in the main frame. If you choose one of the Option links, rather than a Page link, you'll see the left frame refresh with a list of the remaining options, and the right frame with the actual page you selected. If you watch closely, you'll see that the left frame is refreshed a split second before the right frame is updated. We'll talk about why this happens later in this section.
So that's what this example does: it displays a variety of pages in the main section of a two-pane frameset and keeps the left-hand pane in sync—and it uses both client and server-side scripting to accomplish this.
If you've designed a site with frames before, you know that creating a two-pane frameset with a navigation frame and contents frame isn't too difficult. The frameset HTML file contains the size and URL information for the frames, so that they can be created and laid out. Each of the pages is a separate file, as specified in the frameset file, and these are loaded into the frames just created.
Once the pages have been rendered, new pages are targeted to a specific frame by adding the
TARGET attribute to the links that modify the other frame. For example, our frameset has two frames called
MainFrame. To change the contents of
MainFrame from a click on a link in
NavBar, we'd just use:
<A HREF="mypage.htm" TARGET="MainFrame">Go To My Page</A>
However, with this method, the left-hand frame doesn't change to reflect what is shown in the main frame. Our example does allow this, and this significant improvement comes from the combination of ASP and client-side code.
Where would we use something like this? The answer is any place that we currently use (or could use) frames, but want to give the viewer of our site more feedback as to what is happening. Or perhaps we'd like to customize the available options based on which user is accessing our site, or what part of the site they're viewing. We can easily extend this example to the specific needs of our site, but first we should understand how the code works.
How It Works
In contrast to the first two examples (and the next one), this sample consists of more than a few files, although the interesting work takes place primarily in two files:
navbar.asp. It's no coincidence that these files are the only Active Server pages of the whole lot. The rest of the pages are simple HTML files—that in the real, non-book-example, world would hold the site's content. So we can keep things straight, all of the files are listed below:
| ||The top-level frameset page|
| ||Navigation bar (left-hand pane) page|
| ||Page one|
| ||Page two|
| ||Page three (with option links)|
| ||Option page one|
| ||Option page two|
| ||Option page three|
| ||Option page four|
Creating the Frameset with ASP
We first need to understand how the
frameset.asp page works. This is the page you loaded into your browser earlier, and it's where everything starts. The ASP code in the page sets the values of two variables,
MainFramePage, depending on the value passed to the page in the URL. We'll see this in a while—just accept for now that they are set up correctly. So,
frameset.asp sets up the frameset with different pages:
... the ASP code that sets NavBarPage and MainFramePage goes here ... <HTML> <HEAD> <TITLE> Frameset Demonstration </TITLE> </HEAD> <FRAMESET COLS="150,*"> <FRAME NAME="NavBar" SRC="<%= NavBarPage %>" SCROLLING="AUTO"> <FRAME NAME="MainFrame" SRC="<%= MainFramePage %>" SCROLLING="AUTO"> </FRAMESET> </HTML>
COLS="150,*"> line creates a frameset consisting of two columns. The width of the left-hand column is
150 pixels, and the right hand column takes up the remainder. The two
<FRAME> tags provide the information about each frame. The
NAME attribute defines the frame's name, and is important because it's what we use in our links and script code to refer to the frame. The
SCROLLING attribute turns scrolling on if needed. But what of the
SRC attribute with that familiar ASP code string
<%= ... %> ?
Selecting the Pages to Display
We're telling ASP to insert whatever is in the variables
MainFramePage into the
<FRAME> tags. The important thing is how these variables are set, and to understand this we need to look at the code we didn't show in the listing above. The page expects to find an argument in the query string, containing a parameter called
Nav. In other words, it expects to be called with a URL like this:
Here's the code itself:
<% 'get Nav and use it to determine which frames to display 'and which parameter to call navbar.asp with NavChoice = Request.QueryString("Nav") Select Case NavChoice Case "pageone" NavBarPage = "navbar.asp?BarChoice=pageone" MainFramePage = "pageone.htm" Case "pagetwo" NavBarPage = "navbar.asp?BarChoice=pagetwo" MainFramePage = "pagetwo.htm" Case "pagethree" NavBarPage = "navbar.asp?BarChoice=pagethree" MainFramePage = "pagethree.htm" Case "all" NavBarPage = "navbar.asp?BarChoice=all" MainFramePage = "main.htm" Case Else 'choose all Response.Redirect "frameset.asp?nav=all" End Select %> ... HTML code is here ...
The first line of real code uses the
QueryString collection of the
Request object to get the value of the
Nav parameter, if it exists, from the URL. We store that value in the variable called
NavChoice. With this information the code uses a
Case statement to set the variables depending on what was specified in the URL. By using
all, we can ask for the main content page (
main.htm) to be displayed in the main frame, and the navigation bar to show links to all three pages.
If there is no value for
Nav in the query string, as when we first loaded the page, the
Else part of the
Select Case construct is executed. This uses the
Redirect method to refresh the page, showing the main content page and all of the links. Of course, we could have copied the code down from the
all clause immediately above it—it has exactly the same effect. However, this is better style because it means we only have to change our code in one place if we decide to modify the default action.
Looking at the clauses themselves, it's easy to understand what they are doing. They put the name of the file that should be loaded into the main frame into
MainFramePage. When our
Nav option is
pageone it's logical to think that
pageone.htm should be the page we see, and so on. However, notice that setting the
NavBarPage variable is a little different—we're loading the same file
navbar.asp in each case. What our code does, however, is add a different parameter to the query string each time.
The Dynamic Navigation Bar
This is because the
navbar.asp file changes its behavior, depending on the
BarChoice value included in the URL. The part of
navbar.asp that we're interested in is this:
This works almost identically to the code in
frameset.asp. First it stores the value of the
BarChoice part of the URL in a variable of the same name, and then uses this variable in a
Case construct to output the appropriate set of
<A> tags. When
pageone, the code only outputs links to
pagethree.htm. The code for the
all options is nearly identical to the code here, so we haven't listed it. You can check it out in the
Changing Both Frames with Client-Side Code: Method One
Now that we've talked about how the two most important pages are created, we can discuss that new enigma we've unearthed: the use of
navbar.asp. As we said earlier, it's possible to change the contents of one frame, when a link in another is clicked, by using the
TARGET attribute in normal HTML. However, in our case, what we want to do is update both of the frames in our frameset, and HTML doesn't provide a way to do this. Fortunately, our task can be accomplished with a little client-side code. We show two different methods to accomplish this in the sample, and we're ready to discuss the first now.
If you remember back to the object model discussion in the last chapter, you'll recall the
Frames collection of the
Document object, and the
Frames provides an interface to each frame in a frameset, while the
Location object gives information about the current page displayed in the frame. Each frame and window has a
Location object. It's no surprise, then that two lines of code can change the contents of both of our frames:
parent.frames("NavBar").location.href = "navbar.asp?BarChoice=pageone" parent.frames("MainFrame").location.href = "pageone.htm"
We need to remember that, to access the
Location objects of the frames in our frameset, we need to go back one step to the top-level frame. The
Frames collection of the
MainFrame frames are both empty because these frames don't have any sub-frames. However, the parent frame of
MainFrame includes both
MainFrame. In the code above we use the
Parent property of the (default)
Window object to access the correct collection. Once we have the reference to the correct frame, setting the
HRef property of the
Location object causes the current window to display whatever URL is specified.
The only thing we haven't talked about is how the code above actually gets executed. Normally we connect client-side code to an event raised by an object. For example, we might execute code when form button is clicked, i.e. the
onClick event is fired. In this case we'd like to execute the code when a link is clicked. Link objects do have an event called
onClick, and we could specify that our relocation code be executed in response to this event. We're not going to use it in this case, because then we'd have to follow the unsightly practice of specifying an
HREF attribute whose value is the empty string (because our code would be doing all the work). Holding the mouse over a link with no value can confuse viewers who depend on the status bar to see where they're going. In addition, in a more graphical site we might want to do the same thing but with an image map, and these don't have
onClick events like
Link objects do. Instead we'll directly specify the code to executed in the
HREF attribute. This is where the
We've been using VBScript in the last few chapters, but Internet Explorer doesn't support the
vbscript:subroutine-name syntax. It does support the equivalent
navbar.asp we haven't see yet contain these routines:
Putting this all together, we can see that clicking the Page One link causes the browser to execute the code in the
GoPageOne subroutine, and this code loads
pageone.htm into the main frame and reloads
BarChoice equal to
pageone. The rest of the Page links follow the same format. They call a VBScript function that changes the
HRef properties of both frames to the correct URLs. When
navbar.asp reloads, it changes the page links displayed, and everything is ready to go again.
The Option Links on Page Three
The last major feature of this example, which we haven't talked about yet, is the set of option links on page three. If you haven't already seen these, open up page three by clicking on the appropriate link.
Click on one of the option links in the main frame. The
NavBar frame reloads, displaying links to all three pages and to the options that weren't selected. The page for the option we clicked is displayed in the main frame.
We could have implemented this in the same way we did the page navigation, but we've chosen an alternative method so that we can keep all of the navigation code in
navbar.asp. If we used the first method, we would have to add many additional functions to the third page, making the content more difficult to modify independently of the script code, and further melding the site's logic and content—not a good thing.
In addition, suppose we had option arrays like this on each of our three pages. All of a sudden we need to maintain navigation code in four documents instead of one. Finally, as you'll see when we look at the code that implements this in
navbar.asp, we are able to use the similarities between the text strings to move most of the code into a loop, further simplifying our design. In a real world situation we'd be far more likely to use entries from a database to populate this list, but even then the same code can apply—using the unique identifier for each database record instead of our arbitrary series of options from 1 to 4.
Changing Both Frames with Client-Side Code: Method Two
Those are the benefits, but how exactly does this second scheme work? Our first clue is the
HREF attribute of each of the links on page three. Here's the code for the first link:
<LI><A TARGET="NavBar" HREF="navbar.asp?BarChoice=onelink&id=1">Option One</A>
We're using the
TARGET attribute, which is the good old HTML-only way of targeting a page with a frame different to the one containing the link. The HTML here changes the contents of
NavBar (which, since this code is in
MainFrame, is the other frame) to the URL
"navbar.asp?BarChoice=onelink&id=1". This code doesn't do anything about changing the page loaded into
MainFrame, yet the page itself does indeed change.
It's only possible to directly change one frame or window with the
TARGET attribute of the anchor tag. However, with some strategically placed client-side code, we can change more than that. In our example, code in
navbar.asp is changing the contents of
MainFrame, using the value of the
onelink in this case—to determine which block of code to execute. The value
id parameter that follows it is used by the
onelink code, as we'll see in a moment. Think about that for a second. The sequence is:
Click on a link in frame
MainFramethat points to
navbar.asppage reloads and
For example, the first link on page three passes the query string
BarChoice=onelink&id=1 to the navigation bar file
navbar.asp. We've already looked at the
pagexxx values of the
BarChoice parameter that this code can handle, so let's look now at the final choice,
Case clauses for the other pages consisted of a block of HTML. The clause for
onelink is a little more complicated, including two sets of ASP script in addition to the same HTML. We'll start with the familiar and move quickly into new territory.
The Navigation Bar 'onelink' Code
We see the same old table listing page options at the top of the finished
onelink frame, and it's no surprise—the first visible HTML generated by
navbar.asp for this option is the same as we've seen before in the other categories. After this table we display the list of options that aren't currently displayed. If we're currently showing
Option3.htm in the main frame, we'd like to display links to options one, two, and four in the navigation bar. Our ASP code for this looks like:
<% For i = 1 to 4 If CInt(CurrOption) <> i Then 'print link %> <A HREF="navbar.asp?barchoice=onelink&id=<%= i %>"> Option <%= i %></a><p> <% End If Next %>
And it generates this HTML:
<A HREF="navbar.asp?barchoice=onelink&id=1">Option 1</a><p> <A HREF="navbar.asp?barchoice=onelink&id=2">Option 2</a><p> <A HREF="navbar.asp?barchoice=onelink&id=4">Option 4</a><p>
The code loops once for each element in our array of options, printing out a link every time, except for when the value stored in
CurrOption is the same as our loop index. At the top of the
onelink code we set a local variable called
CurrOption to the value of the id portion of the query string. So calling
onelink with an additional
CurrOption to be set to
3. This is the first use of the
id parameter we've talked about—the second is in the code that loads the main frame.
Creating VBScript Code Dynamically
If you've looked at the entire code for the
onelink clause you noticed this somewhat nasty looking section right at the beginning of the block:
Response.Write "<SCRIPT LANGUAGE=" & Chr(34) & "VBScript" & Chr(34) & ">" _ & Chr(13) & Chr(10) Response.Write "<!--" & Chr(13) & Chr(10) Response.Write "Sub Window_OnLoad()" & Chr(13) & Chr(10) Response.Write "On Error Resume Next" & Chr(13) & Chr(10) strTemp = "Parent.frames(" & Chr(34) & "MainFrame" & Chr(34) _ & ").location.href = " & Chr(34) & "Option" & CurrOption _ & ".htm" & Chr(34) Response.Write strTemp Response.Write Chr(13) & Chr(10) Response.Write "End Sub" & Chr(13) & Chr(10) Response.Write "-->" & Chr(13) & Chr(10) Response.Write "</SCRIPT>" & Chr(13) & Chr(10)
But, before we let ourselves be scared away by this mess, take a look at the nice and simple HTML code it generates for the browser:
<SCRIPT LANGUAGE="VBScript"> <!-- Sub Window_OnLoad() On Error Resume Next Parent.frames("MainFrame").location.href = "Option3.htm" End Sub --> </SCRIPT>
What we have here is a block of ASP code on the server that generates a block of code that is to be executed on the client. This is very powerful—we're actually changing our client-side code 'on the fly' to suit our purposes.
Let's first understand how
navbar.asp is using this code to reload
MainFrame, and then we'll talk a little more about the ASP code that generated the code in the first place. The client-side code we end up with is relatively simple. Immediately after the HTML page has finished loading, the
Window_onLoad event fires. In the code that is executed, we're using the
Frames collection and
Location object to load the contents of
Option3.htm into our main frame. The only wrinkle is the handy placement of
Next to avoid any unsightly errors in the navigation frame if a non-existent URL is accidentally specified in the next line.
All the ASP code does is to generate this small four-line subroutine by using a series of
Response.Write calls. It uses
Chr(34) to generate double quotes that can't be specified directly in the code because they will be interpreted as server-side code, and the line feeds and carriage returns so the final output is neatly placed on separate lines.
Moving the Code to Our Own Site
That's all for this sample. We've covered a lot in the last few pages, and really showed how ASP can be used with client-side code to generate pages that weren't possible with just HTML or client-side code alone.
In moving the concepts in this example to your own site, you would keep much of the existing
navbar.asp code, modifying it to point to the actual files in your site. The rest of the files (
option*) would be replaced by your content.