Netscape's interpreted scripting language remains a frustrating work in progress.
Rex Baldazo
Editor's note:
Jon Udell is working on a special assignment this month (see the cover story "Your Business Needs the Web"). He will resume writing this column next month.
At the BYTE Site, navigation is always on our minds. As our article archive gets larger, being able to help people browse or do research becomes more important. On the other
hand, we have to make sure that any navigation tools we add won't undo the entire site.
I was thinking about these issues recently, and an epiphany hit: Why not build a small navigation window using Netscape's JavaScript?
The reasons to explore JavaScript were obvious. Besides the
potential of producing a navigation tool, I might uncover traps and pitfalls that I could apply to other JavaScript development projects.
But I found no shortage of pitfalls: As I learned how to program in JavaScript, I also discovered its limitations and the disappointing state of Netscape's documentation for the language.
Not Another Java
While Java and JavaScript are related, it's a distant relationship. Java is a compiled and strongly typed general-purpose language, while JavaScript is an interpreted and specific-purpose scripting language. JavaScript's designers aimed its features directly at manipulating Web pages and at interacting with the user.
By the time you read this, Netscape Navigator 3.0 will be available. But I'm experimenting in 2.0, and, for the most part, these scripts should work in 3.0.One of the nicer navigating ideas Jon Udell has come up with is the notion of a tabbed menu line (for an example, see our editorial calendar at
http://www.byte.com/admin/edit96.htm
). So, I began my grand JavaScript adventure by emulating that look in a month-picker (
see the screen
). My plan was to eventually move the picker to a separate frame so that when you clicked on a month, it would update the other frame with the selected issue. But first I wanted to work in a single window.
The Hypertext Markup Language (HTML) page for the month-picker is
http://www.byte.com/js_menu.htm
. But you can't run the script from our server--you'll need to copy it elsewhere to play with it. More on that later.
Every time you load this page, the init
ial
if
statement checks to see if Navigator has passed along parameters in the search uniform resource locator (URL). The browser creates the search object if the URL is a query, such as http://www.byte.com/js_menu.htm?6. But the page itself creates these URLs in the first place.
The
PrintMonthArray
function (
see the listing
) makes an
<href>
tag for each month except the one that's currently selected. The
<href>
s are circular references back to the js_menu.htm page. The search object passes a number identifying which month you clicked. The browser redraws and reinterprets the page when you click on a month, and thus updates the currently selected month.
This discussion has probably raised two questions in your mind. First, why all this coding gymnastics to do what seems to be a simple window-redraw procedure? Second, why won't this action work from The BYTE Site? The answers to both questions rest squarely on the shoulde
rs of Microsoft and Netscape.
JavaScript's documentation describes a
document.clear()
method that I should have been able to use to clear a document. Then I would have used two other built-in methods,
document.write()
and
document.writeln()
, to redraw the document. That would have been easier, and more direct, than using the search-object contortion.
Unfortunately, I haven't been able to get
document.clear()
to work as advertised on Navigator 2.0, 2.01, or 2.02 on either Windows NT or the Mac OS. I've also tried with the Atlas preview on NT. I haven't had a chance to experiment with JavaScript on Microsoft Internet Explorer 3.0, but it will be interesting to see if Microsoft can get it to work right.
This page doesn't work on The BYTE Site, but it works just fine on most of the servers that I've tried--WebStar (both the Macintosh and the Windows 95/NT versions), Netscape's FastTrack server, and even the little Web server that comes with Microsoft FrontPage
. However, when I moved it over to The BYTE Site, which is powered by Microsoft's Internet Information Server, I kept getting errors. Depending on the security configuration of IIS, I got either a 403 (execute permission denied) or a 404 (not found) error. It's clear that IIS is trying to actually execute the HTML page.
According to Microsoft, the Internet specification on how to deal with queries is open to interpretation. Most vendors have settled on a common practice, while Microsoft took its own approach. That's why my script worked on most other servers but failed on Microsoft IIS. But bowing to the pressures of the market, version 2.0 of IIS will adhere to industry practice. The next version of IIS will ship with NT 4.0, so it may not be available when this article sees print, although you may be able to download a beta version from Microsoft's Web site.
One last point before we leave this script. You will notice that I use a little
MakeArray
function to create an array of objects.
Navigator 3.0 provides a true array object, but for backward compatibility I'm using the older method.
Building a Compass
Having my client-side script fail to execute on the Microsoft IIS server unnerved me. But I had gained enough confidence in JavaScript to create the
BYTECompass
.
For this project, I decided I'd go whole hog, complete with frames and floating windows. The BYTECompass itself is a floating window that allows you to either browse articles by issue or run a search of BYTE's article archive. You can try out the BYTECompass by visiting
http://www.byte.com/byte_js.htm
.
When you point your browser at this file, the main window of your browser divides into two frames (see the screen above). The upper frame, which I call the
menu frame
, has a single button that you use to open or close the BYTECompass window. The lower frame points initially at the normal BYTE Site home page. As you select items in the BYTECompass, this lower frame points to the selected issue or search result. Thus, I call it the
art frame
, short for article frame.
This little navigation aid fulfills my main goal because it doesn't require any reworking of the existing BYTE Site. It's a totally transparent add-on for those who have a JavaScript-capable browser.
The byte_js.htm page has the functions to open and close the BYTECompass window. This page also has the code that draws the button in the menu frame. The frameset in byte_js.htm (see the listing
"Main Frameset"
) points the menu frame to a document called blank.htm. That page is essentially an empty HTML page, except for a single JavaScript statement invoking the
CallBack
function in the parent document.
The
CallBack
function could actually resid
e in the blank.htm document, but it's in the byte_js.htm document for historical reasons. I had tried to create a function using built-in methods, such as
document.clear
, that would draw on the blank document defined by blank.htm. But as I mentioned earlier,
document.clear
doesn't work as advertised, so I couldn't get this to work properly.
The answer was to use the history object. Each frame in a window has a history object, which stores the URLs of all the pages that have been displayed in that frame. Using the
history.go
method, you can tell a frame which page in its history list to display.
By using a value of zero as the argument to
history.go
, you tell the frame to reload the current document. In my case, reloading blank.htm into the menu frame causes the
CallBack
function to be called again. It can then check to see the status of the BYTECompass window--and from that determine whether to print a "Show BYTECompass" or "Hide BYTECompass" label on t
he button.
When you close the BYTECompass window, it notifies the parent document, which updates the Show/Hide button. The
onUnload
event is the trigger that tells the BYTECompass window that it's been closed. I use the
onUnload
event to run the
ImDead
function (see the listing
"The Self-Destruct Function"
).
The
onUnload
event is one of the built-in events, and of course there's a matching
onLoad
event. It's important to realize that
onLoad
and
onUnload
are associated only with body and frameset tags. In other words, you can't have a table object that uses the event. However, it's easy enough to have the body object use the
onUnload
event to perform some task for the table object.
The
ImDead
function has a couple of statements to check whether certain objects are non-null. There are two possibilities: You've either closed the BYTECompass window directly or moved the main browser window off
the byte_js.htm page.
If the BYTECompass window has been closed directly, it nulls out the handle to itself (it appears as if the
window.close()
method doesn't do this automatically, so I included the code to do it in my function), and it then uses the history object of the menu frame to force a reload. The menu frame then updates the menu button with the correct label.
But if the user has moved off the byte_js.htm page, the
KillCompass
function in byte_js.htm forces the BYTECompass window to close. As it closes, there's an
onUnload
event that will, as in the previous case, call the
ImDead
function. But if
ImDead
then tries to manipulate objects in the parent window, they no longer exist and you'll get error messages. Thus, the
if
statements are
protective
--they make sure there are objects to manipulate before trying to manipulate them.
Tips and Tricks
There are a couple of nifty tricks I stumbled upon as I was
playing with JavaScript. First, it can get difficult at times to figure out which built-in object you should be using, especially since there are so many aliases for so many objects. So, it's useful to dump out the properties of an object and see what's going on:
var info;
for (var i in obj)
{info += i + " = "
+ obj[i] + "\n"; };
I apologize about all the semicolons--some of them are extraneous, but I just put them in to be safe. Anyway, this will iterate over an object and return a string with all the properties. I then use a little function window to print this as preformatted HTML to a trace window.
The other trick is hidden frames. If you use a frameset such as
<frameset rows="10%, 90%, *">
, the third and final frame will not actually be displayed on the browser. It
is
there--if you move your mouse over the bottom of the display window, it should turn into a double-headed arrow. This will let you grab the border and lift it so you can actually see
the third frame.
So why would you want a hidden frame? I've sometimes used this hidden frame as a way to attach code, such as my trace/debug functions, to a page. Then when the page finally works, I simply alter the frameset to remove the hidden page. You can also use this as a way to preload graphics in the background.
The more I've used JavaScript, the less enamored with it I've become. I spent several years as an Ada programmer; say what you will about the language, but if the
Language Reference Manual
(LRM) says that Ada behaves a certain way, then, by God, it
always
behaves that way.
The same cannot be said of the JavaScript documentation from Netscape. Call me old-fashioned, but I expect a vendor's own documentation to at least be correct, and ideally complete as well. If you can't get a feature like
document.clear()
to work, then remove it from the documentation so people won't try to use it.
For furth
er Java Information:
Gamelan
http://www.gamelan.com
Ask the JavaScript Pro
http://www.inquiry.com/techtips/js_pro
JavaScript Sourcebook
http://gmccomb.com/javascript
TOOLWATCH
Open Transport 1.1 (free with System 7.5 update 2)
Apple Computer, Inc.
http://www.info.apple.com/
Finally, a stable and PowerPC-native Macintosh Open Transport networking component. It has vastly improved the performance of our Apple Workgroup Server 6150/66, and it works just fine with our NT DHCP host.
BOOKNOTE
CGI Programming on the World Wide Web
by Shishir Gundavaram
O'Reilly & Associates, Inc.
Internet:
http://www.ora.com/
Price: $29.95
Good coverage of an arcane but terribly useful art. Full of excellent examples.
<frameset rows="10%,*" onUnload="KillCompass();">
<frame src="/blank.htm" name="menu" scrolling="auto"
marginwidth=8 marginheight=8>
<frame src="http://www.byte.com/" name="art"
scrolling="auto" marginwidth=0 marginheight=0>
</frameset>
The frameset points to an HTML page with a single
JavaScript statement that invokes the callback
function in the parent document.
function ImDead() {
if (rootWin.parent != null) {
rootWin.parent.BYTECompass = null;
if (rootWin.parent.menu != null)
{ rootWin.parent.menu.history.go(0);
};
}
The ImDead function in the BYTECompass uses a couple
of statements to check for non-null objects.
photo_link (20 Kbytes
)

screen_link (15 Kbytes)

Emulate the tabbed menu line (
top
) in JavaScript (
bottom: "PrintMonthArray Function"
), and you'll uncover technical flaws.
screen_link (13 Kbytes)

The BYTECompass offers two frames: The single button opens and closes the window; the larger frame points to search results.
For the past year, technical editor Rex Baldazo has worked with BYTE's New Media group to help develop The BYTE Site. He recently moved to the reviews department. He can be reached at
rbaldazo@bix.com
.