This application involves two files: index.html
and multi.html
.
index.html,
shown in Example 4.1, utilizes nested framesets to achieve the
surrounding border effect.
Example 4-1. index.html
1 <HTML> 2 <HEAD> 3 <TITLE>Multi-Search Engine Interface Site</TITLE> 4 <SCRIPT LANGUAGE="JavaScript1.2"> 5 <!-- 6 var black = '<BODY BGCOLOR=BLACK></BODY>'; 7 var white = '<BODY BGCOLOR=WHITE></BODY>'; 8 //--> 9 </SCRIPT> 10 </HEAD> 11 <FRAMESET ROWS="15,*,50" FRAMEBORDER=0 BORDER=0> 12 <FRAME SRC="javascript: parent.black;" SCROLLING=NO> 13 <FRAMESET COLS="15,*,15" FRAMEBORDER=0 BORDER=0> 14 <FRAME SRC="javascript: parent.black;"SCROLLING=NO> 15 <FRAME SRC="javascript: parent.white;"> 16 <FRAME SRC="javascript: parent.black;" 17 SCROLLING=NO> 18 </FRAMESET> 19 <FRAME SRC="multi.html" SCROLLING=NO> 20 </FRAMESET> 21 </HTML>
The two JavaScript variables black and
white at lines 6 and 7 are evaluated as HTML
strings in the SRC
attribute of the frames in lines
12 and 14-16. We reviewed this in the sidebar JavaScript Technique: Cheating the SRC Attribute in Chapter 2. If you’ve
been following along in order, this shouldn’t be rocket science. The
only frames that see any real action are frames[2]
,
which displays the search results, and frames[4]
,
which houses the search engine interface. The rest are purely for
show. Let’s move on to multi.html,
shown in
Example 4.2.
Example 4-2. multi.html
1 <HTML> 2 <HEAD> 3 <TITLE>Multi-Engine Menu</TITLE> 4 <SCRIPT LANGUAGE="JavaScript1.2"> 5 <!-- 6 7 parent.frames[2].location.href = 'javascript: parent.white'; 8 9 var NN = (document.layers ? true : false); 10 var curSlide = 0; 11 var hideName = (NN ? 'hide' : 'hidden'); 12 var showName = (NN ? 'show' : 'visible'); 13 var perLyr = 4; 14 var engWdh = 100; 15 var engHgt = 20; 16 var left = 375; 17 var top = 10; 18 var zIdx = -1; 19 var imgPath = 'images/'; 20 var arrayHandles = new Array('out', 'over'); 21 22 for (var i = 0; i < arrayHandles.length; i++) { 23 eval('var ' + arrayHandles[i] + ' = new Array()'); 24 } 25 26 var engines = new Array( 27 new Array('HotBot', 28 'http://www.hotbot.com/?MT=', 29 'http://www.hotbot.com/'), 30 new Array('InfoSeek', 31 'http://www.infoseek.com/Titles?col=WW&sv=IS&lk=noframes&qt=', 32 'http://www.infoseek.com/'), 33 new Array('Yahoo', 34 'http://search.yahoo.com/bin/search?p=', 35 'http://www.yahoo.com/'), 36 new Array('AltaVista', 37 'http://www.altavista.com/cgi-bin/query?pg=q&kl=XX&q=', 38 'http://www.altavista.digital.com/'), 39 new Array('Lycos', 40 'http://www.lycos.com/cgi-bin/pursuit?matchmode=and&cat=lycos' + 41 '&query=', 42 'http://www.lycos.com/'), 43 new Array('Money.com', 44 'http://jcgi.pathfinder.com/money/plus/news/searchResults.oft?' + 45 'vcssortby=DATE&search=', 46 'http://www.money.com/'), 47 new Array('DejaNews', 48 'http://www.dejanews.com/dnquery.xp?QRY=', 49 'http://www.dejanews.com/'), 50 new Array('Insight', 51 'http://www.insight.com/cgi-bin/bp/870762397/web/result.html?' + 52 'a=s&f=p&t=A&d=', 53 'http://www.insight.com/'), 54 new Array('Scientific American', 55 'http://www.sciam.com/cgi-bin/search.cgi?' + 56 'searchby=strict&groupby=confidence&docs=100&query=', 57 'http://www.sciam.com/cgi-bin/search.cgi'), 58 new Array('Image Surfer', 59 'http://isurf.interpix.com/cgi-bin/isurf/keyword_search.cgi?q=', 60 'http://www.interpix.com/'), 61 new Array('MovieFinder.com', 62 'http://www.moviefinder.com/search/results/1,10,,00.html?' + 63 'simple=true&type=movie&mpos=begin&spat=', 64 'http://www.moviefinder.com/'), 65 new Array('Monster Board', 66 'http://www.monsterboard.com/pf/search/USresult.htm? ' + 67 'loc=&EmploymentType=F&KEYWORDS=', 68 'http://www.monsterboard.com/'), 69 new Array('MusicSearch.com', 70 'http://www.musicsearch.com/global/search/search.cgi?QUERY=', 71 'http://www.musicsearch.com/'), 72 new Array('ZD Net', 73 'http://xlink.zdnet.com/cgi-bin/texis/xlink/xlink/search.html?' + 74 'Utext=', 75 'http://www.zdnet.com/'), 76 new Array('Biography.com', 77 'http://www.biography.com/cgi-bin/biomain.cgi?search=FIND&field=', 78 'http://www.biography.com/'), 79 new Array('Entertainment Weekly', 80 'http://cgi.pathfinder.com/cgi-bin/ew/cg/pshell?venue=pathfinder&q=', 81 'http://www.entertainmentweekly.com/'), 82 new Array('SavvySearch', 83 'http://numan.cs.colostate.edu:1969/nph-search?' + 84 'classic=on&Boolean=OR&Hits=10&Mode=MakePlan&df=normal' + 85 '&AutoStep=on&KW=', 86 'http://www.savvysearch.com/'), 87 new Array('Discovery Online', 88 'http://www.discovery.com/cgi-bin/searcher/-?' + 89 'output=title&exclude=/search&search=', 90 'http://www.discovery.com/'), 91 new Array('Borders.com', 92 'http://www.borders.com:8080/fcgi-bin/db2www/search/' + 93 'search.d2w/QResults?doingQuickSearch=1&srchPage=QResults' + 94 '&mediaType=Book&keyword=', 95 'http://www.borders.com/'), 96 new Array('Life Magazine', 97 'http://cgi.pathfinder.com/cgi-bin/life/cg/pshell?' + 98 'venue=life&pg=q&date=all&x=15&y=16&q=', 99 'http://www.life.com/') 100 ); 101 102 engines = engines.sort(); 103 104 function imagePreLoad(imgName, idx) { 105 for(var j = 0; j < arrayHandles.length; j++) { 106 eval(arrayHandles[j] + "[" + idx + "] = new Image()"); 107 eval(arrayHandles[j] + "[" + idx + "].src = '" + imgPath + 108 imgName + arrayHandles[j] + ".jpg'"); 109 } 110 } 111 112 function engineLinks() { 113 genLayer('sliderule', left - 20, top + 2, 25, engHgt, true, 114 '<A HREF="javascript: changeSlide(1);" ' + 115 'onMouseOver="hideStatus(); return true;">' + 116 '<IMG SRC="' + imgPath + 'ahead.gif" BORDER=0></A><BR>' + 117 '<A HREF="javascript: changeSlide(-1);" ' + 118 'onMouseOver="hideStatus(); return true;">' + 119 '<IMG SRC="' + imgPath + 'back.gif" BORDER=0></A>'); 120 lyrCount = Math.ceil 121 (engines.length / perLyr); 122 for (var i = 0; i < lyrCount; i++) { 123 var engLinkStr = '<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0><TR>'; 124 for (var j = 0; j < perLyr; j++) { 125 var imgIdx = (i * perLyr) + j; 126 if (imgIdx == engines.length) { break; } 127 var imgName = nameFormat(engines[imgIdx][0]); 128 imagePreLoad(imgName, imgIdx); 129 engLinkStr += '<TD><A HREF="javascript: ' + 130 'callSearch(document.forms[0].elements[0].value, ' + 131 imgIdx + ');" onMouseOver="hideStatus(); imageSwap(\'' + 132 imgName + '\', ' + imgIdx + ', 1); return true" ' + 133 'onMouseOut="imageSwap(\'' + imgName + '\', ' + imgIdx + 134 ', 0);"><IMG NAME="' + imgName + '" SRC="' + imgPath + imgName + 135 'out.jpg' + '" BORDER=0></A></TD>'; 136 } 137 engLinkStr += '</TR></TABLE>'; 138 genLayer('slide' + i, left, top, engWdh, engHgt, false, engLinkStr); 139 } 140 } 141 142 function genLayer(sName, sLeft, sTop, sWdh, sHgt, sVis, copy) { 143 if (NN) { 144 document.writeln('<LAYER NAME="' + sName + '" LEFT=' + sLeft + 145 ' TOP=' + sTop + ' WIDTH=' + sWdh + ' HEIGHT=' + sHgt + 146 ' VISIBILITY=' + sVis + ' z-Index=' + (++zIdx) + '>' + 147 copy + '</LAYER>'); 148 } 149 else { 150 document.writeln('<DIV ID="' + sName + 151 '" STYLE="position:absolute; overflow:none;left: ' + 152 sLeft + 'px; top:' + sTop + 'px; width:' + sWdh + 'px; height:' + 153 sHgt + 'px; visibility:' + sVis + ' z-Index=' + (++zIdx) + 154 '">' + copy + '</DIV>'); 155 } 156 } 157 158 function nameFormat(str) { 159 var tempArray = str.split(' '); 160 return tempArray.join('').toLowerCase(); 161 } 162 163 function hideSlide(name) { refSlide(name).visibility = hideName; } 164 165 function showSlide(name) { refSlide(name).visibility = showName; } 166 167 function refSlide(name) { 168 if (NN) { return document.layers[name]; } 169 else { return eval('document.all.' + name + '.style'); 170 } 171 172 function changeSlide(offset) { 173 hideSlide('slide' + curSlide); 174 curSlide = (curSlide + offset < 0 ? slideShow.length - 1 : 175 (curSlide + offset == slideShow.length ? 0 : curSlide + offset)); 176 showSlide('slide' + curSlide); 177 } 178 179 function imageSwap(imagePrefix, imageIndex, arrayIdx) { 180 document[imagePrefix].src = eval(arrayHandles[arrayIdx] + 181 "[" + imageIndex + "].src"); 182 } 183 184 function callSearch(searchTxt, idx) { 185 if (searchTxt == "") { 186 parent.frames[2].location.href = engines[idx][2] + 187 escape(searchTxt); 188 } 189 else { 190 parent.frames[2].location.href = engines[idx][1] + 191 escape(searchTxt); 192 } 193 } 194 195 function hideStatus() { window.status = ''; } 196 197 //--> 198 </SCRIPT> 199 200 </HEAD> 201 <BODY BGCOLOR="BLACK" onLoad="showSlide('slide0');"> 202 <SCRIPT LANGUAGE="JavaScript1.2"> 203 <!-- 204 engineLinks(); 205 //--> 206 </SCRIPT> 207 <FORM onSubmit="return false;"> 208 <TABLE CELLPADDING=0> 209 <TR> 210 <TD> 211 <FONT FACE=Arial> 212 <IMG SRC="images/searchtext.jpg"> 213 </TD> 214 <TD> 215 <INPUT TYPE=TEXT SIZE=25> 216 </TD> 217 </TR> 218 </TABLE> 219 </FORM> 220 </BODY> 221 </HTML>
More than 200 lines of code, but you’ve seen most of it already. This shouldn’t be that bad. Let’s begin at line 7.
parent.frames[2].location.href ='javascript: parent.white';
If you count the frames in index.html
,
you’ll see that frames[2]
is where the
search results show up. Setting the location.href
property in this frame makes things a bit smoother if you
decide to reload the application. This automatically sets the results
document content to some “local” HTML so that you
don’t have to wait for any previous search queries to reload.
By the way, even though you get a neat display of search engine
results in frames[2]
, once you follow a search
results link, you’re at the mercy of the search engine
designers. Some will let you follow the links while staying in the
same frame. Others, unfortunately, like InfoSeek, will force the
document into the top window of the browser.
Let’s take a trip down Memory Lane (RAM, in case you’re
wondering). As you examine the variables below, you’ll see some
newcomers, but several will bear a striking resemblance to those
you’ve worked with in Chapter 3.
Look, there’s NN and
curSlide
! And they brought
hideName
and showName
, too.
Not to mention imagePath
and zIdx
:
var NN = (document.layers ? true : false); var curSlide = 0; var hideName = (NN ? 'hide' : 'hidden'); var showName = (NN ? 'show' : 'visible'); var perLyr = 4; var engWdh = 100; var engHgt = 20; var left = 375; var top = 10; var zIdx = -1; var imgPath = 'images/'; var arrayHandles = new Array('out', 'over');
These variables all have the same function they did in Chapter 3. They just pick up where they left
off. As for the new ones, perLyr
defines the number of search engines you want to display
per layer. Variables engWdh
and engHgt
define default width and height values for each layer,
respectively. Variables left and top
hold values for positioning the layers. Variable
arrayHandles
contains an
array used for dynamically preloading images. Hold that thought for
just a bit; we’ll go over it shortly.
Talking about family reunions, the variables aren’t the only familiar code. Check out the functions from way back when.
Lines 142-156:
function genLayer(sName, sLeft, sTop, sWdh, sHgt, sVis, copy) { if (NN) { document.writeln('<LAYER NAME="' + sName + '" LEFT=' + sLeft + ' TOP=' + sTop + ' WIDTH=' + sWdh + ' HEIGHT=' + sHgt + ' VISIBILITY=' + sVis + ' z-Index=' + (++zIdx) + '>' + copy + '</LAYER>'); } else { document.writeln('<DIV ID="' + sName + '" STYLE="position:absolute; overflow:none;left: ' + sLeft + 'px; top:' + sTop + 'px; width:' + sWdh + 'px; height:' + sHgt + 'px; visibility:' + sVis + ' z-Index=' + (++zIdx) + '">' + copy + '</DIV>'); } }
Lines 163-177:
function hideSlide(name) { refSlide(name).visibility = hideName; } function showSlide(name) { refSlide(name).visibility = showName; } function refSlide(name) { if (NN) { return document.layers[name]; } else { return eval('document.all.' + name + '.style'); } } function changeSlide(offset) { hideSlide('slide' + curSlide); curSlide = (curSlide + offset < 0 ? slideShow.length - 1 : (curSlide + offset == slideShow.length ? 0 : curSlide + offset)); showSlide('slide' + curSlide); }
Five functions: genSlide()
,
refSlide()
, hideSlide()
,
showSlide()
, and changeSlide()
.
All of them operate the same way they did in Chapter 3; if you’re not clear on how any
of them works, flip back a chapter and check them out. There are
actually two more functions, imagePreLoad()
and
imageSwap()
, which perform the same operations as
well, but they’ve been modified enough to merit new discussion.
One of the big web paradigms is performing conventionally static operations dynamically. Why do something statically when you can manage it more easily on the fly? That’s what the following code does with image preloading. What’s the typical modus operandi when you want to preload images to perform rollovers? It might look something like this:
var myImage1On = new Image(); myImage1On.src = 'images/myImgOn1.gif'; var myImage1Off = new Image(); myImage1Off.src = 'images/myImgOff1.gif';
Simple enough. But that’s four lines of code for one pair of image rollovers. What if you have five or ten pairs? That’s 20 or 40 lines. If you ever have to make changes, that’ll get messy in no time. The Multiple Search Engine Interface introduces a way to pull off the same preloading, no matter how many (theoretically) image pairs you have. We’ll need three things:
An array of Image objects for each set of images you’ll need. This application uses one array for the images used when the mouse pointer arrow is over the link and one array for the images that roll back when the mouse pointer arrow moves out of the link.
A simple naming convention for the images. The
myImg1On.gif/myImgOff1.gif
convention will work fine. See the sidebar JavaScript Technique: Using Well-Constructed Naming Conventions in Chapter 3 for more information. The naming convention must incorporate the names of the arrays in step 1.The
eval()
method.
For step 1, this application will use two arrays. One will be named
out and will contain Image
objects of those images that roll over when the mouse-pointer arrow
is outside the linked image. The other will be named
over and will contain Image
objects of those images that roll over when the
mouse-pointer arrow is over the linked image. Those variables will be
represented for now in an array of strings called
arrayHandles
, line 20:
var arrayHandles = new Array('out', 'over')
For step 2, we’ll use a very simple naming convention. All
image pairs will have the same prefix followed by either
out.jpg
or over.jpg
,
depending on the image. For example, the image rollovers associated
with InfoSeek are named infoseekout.jpg
and
infoseekover.jpg
.
For step 3, we’ll first iterate through each element of
arrayHandles
and use eval()
to create the arrays soon to hold the Image
objects. Enter lines 22-24:
for (var i = 0; i < arrayHandles.length; i++) { eval('var ' + arrayHandles[i] + ' = new Array()'); }
Performing the above for loop is equivalent to hardcoding this:
var out = new Array(); var over = new Array();
To polish off the preloading, we use eval()
again
in function preLoadImages()
to dynamically create
Image objects and assign the
SRC
property of each. Here is the function in
lines 104-110:
function imagePreLoad(imgName, idx) { for(var j = 0; j < arrayHandles.length; j++) { eval(arrayHandles[j] + "[" + idx + "] = new Image()"); eval(arrayHandles[j] + "[" + idx + "].src = '" + imgPath + imgName + arrayHandles[j] + ".jpg'"); } }
imagePreLoad()
accepts two arguments, a name
prefix (e.g., Infoseek) and an integer used to
assign the appropriate array element to a new
Image object. Once again, a
for loop iterates through
arrayHandles
, utilizing each element string to
access one of the arrays just created and assign it a unique
reference. For example. Calling
imagePreLoad(
'infoseek
',
0)
is equivalent to hardcoding the following:
out[0] = new Image(); out[0].src = 'images/infoseekout.jpg'; over[0] = new Image(); over[0].src = 'images/infoseekover.jpg';
But that’s four lines of code, exactly what I wanted to avoid
doing over and over. Every time I want a new image rollover pair, I
can make a call to preLoadImages()
. And that is
working smarter, not harder.
Variable engines at lines 26-100 represents an array of elements, each containing another array of elements with specific search engine information. Variable engines has 20 fairly long elements, so let’s takes a look at just the first one as shown in lines 27-29:
new Array('HotBot', 'http://www.hotbot.com/?MT=', 'http://www.hotbot.com/'),
Element 0 identifies the search engine name, HotBot. Element 1 identifies the URL with the query string, which, when included with query text, will call the search engine and return the results page. Element 2 represents the URL of the search engine home page. This is used in place of Element 1 if the user attempts a null search (searching with an empty string).
Function engineLinks()
is similar to function
genScreen()
in Chapter 3 because it is responsible for managing
the creation of the layers. It does have its differences, though.
Examine lines 112-140.
The first thing this function takes care of is generating the layer containing the navigation links:
genLayer('sliderule', left - 20, top + 2, 25, engHgt, true, '<A HREF="javascript: changeSlide(1);" '+ 'onMouseOver="hideStatus(); return true;"><IMG SRC="' + imgPath + 'ahead.gif" BORDER=0></A><BR><A HREF="javascript: ' + 'changeSlide(-1);" onMouseOver="hideStatus(); return true;">' + '<IMG SRC="' + imgPath + 'back.gif" BORDER=0></A>');
This happens with a simple call to genLayer()
.
There are no real surprises here. The layer will contain two linked
images: a forward and a backward arrow. Notice that the left and top
pixel values passed in are relative to the left and top positions,
left - 20
and top + 2
, of the
soon-to-be created engine link layers.
Next up, variable lyrCount
determines the number
of layers of search engine buttons to create, depending on the number
of buttons you want per layer and the number of engines you have
allotted in the engines array. It is really
pretty easy. Divide the number of search engines
(engines.length
) by the number of engines you want
to display per layer (perLyr
). If the remainder
is anything but 0, you’ll need one more layer.
Let’s use the values of the application.
engines.length
is 20, and
perLyr
is 4. Therefore, variable
lyrCount
is 5. If I had used 21 engines, 21 / 4
= 5.25. A remainder of .25 indicates the need for an extra layer, so
lyrCount
would be set to 6. Here is the code
again:
lyrCount = Math.ceil(engines.length / perLyr);
The conditional operator performs exactly as described above. If the
remainder is 0, set lyrCount
to engines.length/perLyr
. Otherwise set
lyrCount
to
Math.ceil(engines.length/perLyr)
. Determining
lyrCount
is important. Once
determined, engineLinks()
creates
lyrCount
layers in lines
122-136:
for (var i = 0; i < lyrCount; i++) { var engLinkStr = '<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0><TR>'; for (var j = 0; j < perLyr; j++) { var imgIdx = (i * perLyr) + j; if (imgIdx >= engines.length) { break; } var imgName = nameFormat(engines[imgIdx][0]); imagePreLoad(imgName, imgIdx); engLinkStr += '<TD><A HREF="javascript: ' + callSearch(document.forms[0].elements[0].value, ' + imgIdx + ');" onMouseOver="hideStatus(); imageSwap(\'' + imgName + '\', ' + imgIdx + ', 1); return true" onMouseOut="imageSwap(\'' + imgName + '\', ' + imgIdx + ', 0);"><IMG NAME="' + imgName + '" SRC="' + imgPath + imgName + 'out.jpg' + '" BORDER=0></A></TD>'; }
For each layer, engineLinks()
declares local
variable engLinkStr
, which will contain the code
for each slide. After creating engLinkStr
, which
as you can see in line 123 starts the table that will encapsulate the
images, a nested for loop makes
perLyr
iterations to create
the table cells that will contain the image.
For each perLyr
iteration,
local variable imgIdx
is
assigned the value (i
* perLyr)
+
j
. That expression is simply
an integer that starts at
and is incremented by 1 at the beginning of every iteration.
imgIdx
will be used to
identify the prefix of the images (which is the name of the search
engine in element
in each array in engines) and then preload the
images as discussed earlier. Table 4.1 offers a
quick multiplication scheme when
perLyr
is 4.
Table 4-1. Calculating Layers to Display (perLayer is 4)
When i is . . . |
And j values at . . . |
(i * perLyr) + j keeps rising by 1. . . |
---|---|---|
0 |
0, 1, 2, 3 |
0, 1, 2, 3 |
1 |
0, 1, 2, 3 |
4, 5, 6, 7 |
2 |
0, 1, 2, 3 |
8, 9, 10, 11 |
3 |
0, 1, 2, 3 |
12, 13, 14, 15 |
4 |
0, 1, 2, 3 |
16, 17, 18, 19 |
There are 20 integers, 0-19.
Now that we know the value of imgIdx
, we have to
make sure we haven’t gone too far. Line 126 handles that:
if (imgIdx == engines.length) { break; }
Since the value of imgIdx
is incremented unconditionally each iteration, once it
reaches the engines.length
, there are no more
search engines to display links for, so the function will
“break” out of the for loop.
The time has come to preload the pair of images for each search
engine. Before that happens, we need to know the image prefix. Simply
enough, the prefix is the lowercase version of the search engine
name. That is, the “InfoSeek” image prefix is
infoseek; the HotBot image prefix is
hotbot, and so on. Variable
imgIdx
identifies the
correct image prefix in line 127:
var imgName = nameFormat(engines[imgIdx][0]);
Element 0 of each array in engines contains
the search engine name. Variable imgIdx
identifies the correct element index in engines,
which returns that search engine name. All that is left is to convert
all the letters to lowercase. Function nameFormat()
does the trick at lines 158-161:
function nameFormat(str) { var tempArray = str.split(' '); return tempArray.join('').toLowerCase(); }
All whitespace is removed by splitting the string passed by
whitespaces into array elements, then joined. Now
imgName
has a lowercase, whitespace-free image
prefix. It is ready to be passed with imgIdx
to
imagePreload()
in line 128.
Time to build a linked image with appropriate rollover code for each search engine. Enter lines 129-135:
engLinkStr += '<TD><A HREF="javascript: ' + 'callSearch(document.forms[0].elements[0].value, ' + imgIdx + ');" ' + 'onMouseOver="hideStatus(); imageSwap(\'' + imgName + '\', ' + imgIdx + ', 1); return true" onMouseOut="imageSwap(\'' + imgName + '\', ' + imgIdx + ', 0);"><IMG NAME="' + imgName + '" SRC="' + imgPath + imgName + 'out.jpg' + '" BORDER=0></A></TD>';
Let’s consider this. Each search engine link will need the same four requirements:
Dissecting the string set to engLinkStr
will
reveal how each requirement is satisfied.
The first requirement is satisfied with the following code:
HREF="javascript: callSearch(document.forms[0].elements[0].value, ' + imgIdx + ');"
You can see that the link created, upon being clicked, will call
function callSearch()
, in which
document.forms[0].elements[0].value
will be passed
in along with the corresponding value of imgIdx
.
More on callSearch()
soon. For now it’s safe
to say that requirement 1 is in the bag.
The second requirement is satisfied by the following code:
'onMouseOver="hideStatus(); imageSwap(\'' + imgName + '\', ' + imgIdx + ', 1); return true" ' +
This code handles creating the call to
hideStatus()
for clearing the status bar of
annoying URLs, then the call to imageSwap()
,
passing in the three necessary parameters
imgName
, imgIdx
, and an
integer (1) corresponding to the element in
arrayHandles
.
The third requirement is remedied like so:
'onMouseOut="imageSwap(\'' + imgName + '\', ' + imgIdx + ', 0);">' +
Not much of a change. The only appreciable difference is passing in 0 instead of 1.
And now for the fourth requirement:
'<IMG NAME="' + imgName + '" SRC="' + imgPath + imgName + 'out.jpg' + '" BORDER=0></A></TD>';
The name of each image is set to the value of
imgName
. That is how it will be referenced in
function imageSwap()
. The SRC
attribute is set to the concatenation of
imgPath
, imgName
, and
out.jpg
. Since the images will start in the
mouse-arrow pointer out position, the
SRC
tag is set to the images corresponding with
the out.jpg
substring. For example, the opening
image for HotBot is located at
images/hotbotout.jpg.
Lines 137-138 add the finishing touches:
engLinkStr += '</TR></TABLE>'; genLayer('slide' + i, left, top, engWdh, engHgt, false, engLinkStr);
That is, engLinkStr
receives the HTML to close the table, and all that remains
is to create the layer with genLayer()
. Notice
that all calls to genLayer()
here pass in false.
Remember that passing in false
hides the layer
from view. All the layers are hidden until the page is loaded. Then
slide0 is revealed in the
onLoad event handler at line 201.
You saw it Chapter 3, but this version is a bit different. Consider lines 179-182:
function imageSwap(imagePrefix, imageIndex, arrayIdx) { document[imagePrefix].src = eval(arrayHandles[arrayIdx] + "[" + imageIndex + "].src"); }
This function performs the image rollovers. Argument
imagePrefix
identifies the source of the image
to be switched. Arguments imageIndex
and
arrayIdx
are integers that properly access the
correct Image object in
arrayHandles
.
When the HTML form and the layers are in place, the user needs only
enter search text and click the search engine of choice. When users
click a search engine image, function callSearch()
gets the call. Here it is in lines 184-193:
function callSearch(searchTxt, idx) { if (searchTxt == "") { parent.frames[2].location.href = engines[idx][2] + escape(searchTxt); } else { parent.frames[2].location.href = engines[idx][1] + escape(searchTxt); } }
callSearch()
expects two arguments.
searchTxt
contains the text
entered in the text field, and idx
contains an integer that corresponds with the search
engine information in the engines array. The application loads one of
two documents in frames[2]
. If the user enters no
search text, frames[2]
is loaded with the default
home page of the search engine. This URL is contained in element 2 of
each array in engines. If, however, the user enters search text, the
application loads frames[2]
with the URL and query
string of the search engine, plus the escaped version of the text the
user entered.
You might be wondering where I got those lengthy query strings in element 1 of each array in engines. Where could I possibly come up with those values?
Actually, I checked the source code of each search page and built the
query string based on the HTML form used to submit search text.
Let’s start with an easy example. MusicSearch.com has a single
text field for searching. The ACTION
attribute of
the form is http://www.musicsearch.com/global/search/search.cgi.
The name of the field is QUERY. Therefore, the
URL with query string should look like this:
http://www.musicsearch.com/global/search/search.cgi?QUERY= + escape(searchTxt);
That’s pretty easy. One name-value pair. Search engines can have plenty of options, though. Consider the meta-search engine (one that searches other organizations’ databases instead of its own) SavvySearch. With SavvySearch, you enter search text, and then can use checkboxes to choose which media to search, such as search engines, newsgroups, etc. You can also impose Boolean search rules, set the number of results to return from each database, and choose the amount of information displayed about each search result.
The ACTION
attribute of the SavvySearch form is
http://numan.cs.colostate.edu:1969/nph-search.
Here is a list of the required form elements:
The name of the select list for Boolean searches is Boolean
The name of the results count select list is Hits
The name of the result amount radio buttons is df
The name of the text field is KW
I set the Boolean select list value to OR, the
Hits select list value to 10, the results
display button df
value to
normal
, and the search text field
KW to escape(searchTxt)
. I
didn’t invent values OR, 10, and normal
.
Those are either option values in the select lists or radio button
values, all of which are in the HTML source code.
The form also contains two hidden fields, one with the name
Mode, and the other with the name
AutoStep
. The Mode
HIDDEN
field has the value
MakePlan
. The HIDDEN
field
AutoStep
has the value on
.
I’m not sure what purpose they serve, but that’s not
important. All you have to do is add them to the query string.
Submitting a query to SavvySearch, then, requires the following URL:
http://numan.cs.colostate.edu:1969/nph-search? ' + classic=on&Boolean=OR&Hits=10&Mode=MakePlan&df=normal&AutoStep=on&KW= + escape(searchTxt)
Another nice thing about “decrypting” the query strings is that the order of the name-value pairs generally doesn’t matter. As long as they are in the query string, things will work fine.
Get JavaScript Application Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.