I'm a JavaScript newbie.

Sure, I've read the tutorials at webmonkey, and followed along through thread after thread on the mailing lists about 'how can I get my function to insert task here. But I never really got it. Up until last week, the most complex thing I'd ever done with JS was copying other people's scripts and adapting them ever so slightly to work on my page. I was beginning to become very discouraged, but ran into a problem that was just begging to be solved with JavaScript.

I live on Maui, and work as a freelancer running my own very small web-design studio. Most of my work is for local small businesses who are usually under-represented on the web and who typically lack both the resources and understanding to make full use of the internet in their business. My biggest challenge as a designer is making enough money to survive, working on projects with a shoestring budget. My other biggest challenge is pleasing clients, who feel like they are paying WAY TOO MUCH. From talking to other designers and having worked in a local web design company for a year, I know that my rates are incredibly reasonable and that this challenge will always be there when I work with local clients.

Note: I am extremely open to working as a freelance designer/coder for other companies either here or on the mainland :)

Anyway, this amounts to fancy server side solutions (which I would just LOVE

to get into) being generally beyond the reach of many of the projects I am involved

with. I have a hard enough time selling clients on the fact that they need a

new website, much less needing to spend $20/month for PHP or ASP hosting. So,

I usually have to find a way to solve problems that is probably not the best

solution, but one that serves the purpose and does so cheaply and quickly and

requiring a minimum investment. Bummer, but don't feel to sorry for me...I have

it pretty good. :)

The Problem I needed to solve...

Over the course of the past few months, I've been working on a site for a windsurfing company...Simmer Hawaii. This has been a great job, because I somehow managed to talk the client into letting me do whatever I want with their site. When I proposed that the best way for them to be successful on the web is to continually add to the content of the site: they believed me. That means I get to work on this on an ongoing basis, earning pretty good $$ and getting fat discounts on new wave sails and beachwear every now and then.

One of the ideas we had was to add a travel section to the website. Part of

Simmer's business is renting windsurfing gear to vacationing windsurfers. If

you come to Maui to sail, you want to stay on the north shore, because that's

where all the sailing is. There aren't any resorts, there aren't any hotels,

there aren't any condos. On the north shore, you stay in private homes that

are maintained by their owners as vacation rentals. So, this accommodations

reality necessitated featuring many different vacation rental properties on

the website. We also figured there would be a great deal of value in adding

more keyword rich pages of content to the site.

The structure called for a hierarchy of pages, 'site >> travel section >> neighborhood

>> property/unit'. That's how we'd done it in the rest of the site, so we continued

to structure information that way. My

wife's company
, which specializes in vacation accommodations for windsurfers

on Maui's north shore agreed to provide content and handle the inquiries generated.

I have to mention this, because its another thing about the web that I'ma huge

advocate of: using the internet to bring unique businesses together to better

serve the customer public. In this arrangement, Simmer Hawaii gets a) more content

for their website, b) another service that they can offer their customers, c)

a small commission on each sale of a vacation. My wife's company gets a) exposure

for her product/company in another venue, b) more inquiries and an expanded

customer base, c) a small commission on each gear rental sale made, d) the chance

to show her vendors, 'look, I'm marketing you here too'. It's a win/win situation

and a model that I will continue to explore for my customers. But back to the

problem...

The idea behind presenting many options for vacation accommodations in the

travel section was to give visitors to the site as many choices as we can. Each

neighborhood features a number of properties representing a range of price,

size and level of luxury. Trouble is, visitors to the site are likely to want

to inquire about more than one property. I mean, sure that one-bedroom on the

beach at Sprecks might be good...but the studio in Paia is the same price and

closer to town. So, the problem was to provide a mechanism by which users could

add different properties to the list of those that they would inquire about,

and only make them fill out one form to inquire about them all. The limitations

were that the form gets submitted by CGI-email, because that's the only script

available through the cheap-ass

web host

The solution I came up with...

So, how to get those choices about different properties into the CGI-Email

form...I thought about listing each property/unit on the form with a checkbox

beside it and a link back to that property/unit so users could go back for reference.

That seemed a little cumbersome and less than elegant. What I really wanted

was a sort of 'shopping cart', to allow users to choose their properties as

they browsed
. So, I figured if I could 1) put checkboxes next to each property

choice, 2) get the checkboxes to store a value, 3) retrieve the stored values

at the form, 4) use the stored values to print the user's choices into the

form as display text and form input...I'd be looking good. So, how

could I do it?

Because I'm a newbie, this seemed like a really complex thing to do. But it

turned out to be a really perfect 'first software development project'. The

first step is above...define each step of what your script has to do.

How can you write a function or anything until you know what you have to do

at each step? Since my first step called for checkboxes beside each property,

I started by building the property pages for each property/unit. I figured whatever

I came up with function-wise for step two would be the same for each page, and

just a matter of copy & paste.

Step two required a way to store the values for the checked forms. Since most

people are terrified of cookies (and since writing 22 or so values into a cookie

seemed really daunting) I wanted to avoid them. Sort of overkill anyway,

since the state only needs to be remembered until they fill out the form. So,

I chose to split the travel section into frames...one frame for displaying the

content, one for storing the values of the checkboxes. My frameset code (index.html

in the /travel directory) looks like this:

<frameset rows="0,*" cols="*" framespacing="0" 

frameborder="0">

<frame src="frm_hidden_var_storage.html" name="topFrame"

id="topFrame" frameborder="0" scrolling="No"

noresize marginwidth="0" marginheight="0">

<frame src="travel.html" name="mainFrame" id="mainframe"

frameborder="0" scrolling="Auto" noresize marginwidth="0"

marginheight="0">

</frameset>

<noframes>

<body bgcolor="#FFFFFF" text="#000000" topmargin="0"

marginheight="0">

Sorry, you can't see frames...this site wasn't built for you.

</body>

</noframes>

Note: The framset puts the page where the values are stored on top. This

is imperative. Otherwise, the page in the bottom of the frameset is trying to

access objects that don't exist yet. You'll see what I'm talking about when

I get into the functions that make it work...

All that's on the page 'frm_hidden_var_storage.html' is a form with 22 <input

type="hidden" name="[name that corresponds with a property/unit]"

value="false">. When a user checks a checkbox, a function changes

the value of that property/unit's <input type="hidden"... to true.

Like this:

   function updateValue(propName) { 

var hideyElement = eval('parent.topFrame.document.form1.' + propName.name);

hideyElement.value = propName.checked;

}

This function is called by the onClick event handler on the checkbox...onClick="updateValue(this);".

In the first line, I make a variable called hideyElement. Now, I had help with

this part from Erik Mattheis. I don't really understand why I have to eval the

string 'parent.topFrame.document.form1.' before concatenating the argument.name

to it, but it didn't' work when I did it the other way...var hideyElement =

parent.topFrame.document.form1.propName.name;...I got an error telling me that

' parent.topFrame.document.form1.propName' is null or not an object. The conceptual

difference between what JS thinks of as an object, and what it thinks of as

a string is more than I can currently grasp. But thanks to thelist, I was able

to get beyond this point in spite of my limited understanding. The argument

passed to the function is a reference to the checkbox itself. It is important

to note that each checkbox in the site has a name attribute that is exactly

the same as the name attribute for it's corresponding <input type="hidden"...

in the hidden frame. That way, my function can just plug the name value from

the checkbox into the variable that references the hidden input on the other

page and change that objects value attribute to the checked value of the checkbox.

Following? Since 'true' is the value of the checkbox when its checked, I can

change the value of the hidden input to the checked value of the checkbox, and

it evaluates to changing the hidden inputs' value to 'true'.

That was easy enough...now to get those values printed on the page in a useful

format...

One of the very first lessons in my JavaScript tutorials in Danny Goodman's

'JavaScript Bible' (a resource I highly recommend to any newbie wanting to get

their feet wet in JS) deals with looping through an array. Since that intuitively

seemed like the way to get all the values from my hidden inputs, and then do

something based on those values, I started by making an array to loop through:

 var AllProperties = new Array(22);

AllProperties[0] = parent.topFrame.document.form1.tavares_bay_home;

AllProperties[1] = parent.topFrame.document.form1.tropical_hideaway;

AllProperties[2] = parent.topFrame.document.form1.rays_hanawi;

AllProperties[3] = parent.topFrame.document.form1.rays_mauna;

AllProperties[4] = parent.topFrame.document.form1.rays_lua_pele;

AllProperties[5] = parent.topFrame.document.form1.paia_studio;

AllProperties[6] = parent.topFrame.document.form1.kehau;

AllProperties[7] = parent.topFrame.document.form1.kai_hale;

AllProperties[8] = parent.topFrame.document.form1.hookipa_bayview;

AllProperties[9] = parent.topFrame.document.form1.hale_lea_lea1;

AllProperties[10] = parent.topFrame.document.form1.hale_lea_lea2;

AllProperties[11] = parent.topFrame.document.form1.hale_lea_lea3;

AllProperties[12] = parent.topFrame.document.form1.hale_kulani1;

AllProperties[13] = parent.topFrame.document.form1.hale_kulani2;

AllProperties[14] = parent.topFrame.document.form1.hale_kulani3;

AllProperties[15] = parent.topFrame.document.form1.hale_kulani4;

AllProperties[16] = parent.topFrame.document.form1.hale_kulani5;

AllProperties[17] = parent.topFrame.document.form1.aqua_terra1;

AllProperties[18] = parent.topFrame.document.form1.aqua_terra2;

AllProperties[19] = parent.topFrame.document.form1.aqua_terra3;

AllProperties[20] = parent.topFrame.document.form1.aina_lani1;

AllProperties[21] = parent.topFrame.document.form1.aina_lani2;

This array is just a list of all of the <input type="hidden"...

objects on the other, hidden page in the frameset. These objects are the objects

whose values were changed from their default (false) to true in the checkbox

updateValue(propName) function. So now that I have them in an array, I can loop

through that array and find all of the values that are true, like this:

 for (var i = 0; i < AllProperties.length ; i++){

if (AllProperties[i].value == "true"){...

This is one of the most basic constructs in any programming language, what

it does is to make a variable (i), set it to 0. Then it compares the variable

to some other statement, in this case, the length of the AllProperties array

(AllProperties.length), and increments the variable 'i' by one. So, the loop

will run as many times as i < the length of the AllProperties array. And

the value of 'i' always corresponds with an index number of a 22 item array.

Every time it runs, it does something:

 var linkStr = "<a href=\"" + PropLinks[i] + ".html\" 

target=\"_self\">"

document.write(linkStr + PageProperties[i] + "</a><br>")

}

It makes a variable that contains a string ( "<a href=\""),

since I wanted my selected properties to display on the page not merely as text,

but as links back to that properties page in the site. That calls for another

array item, PropLinks[i]. PropLinks[i] is another array I made to serve as a

sort of lookup table for the first array. Its like the AllProperties array,

except that instead of holding a list of objects, it holds a list of strings

that when inserted into this function are included in the output string. So

basically, <a href=\"" + PropLinks[i] + ".html\" target=\"_self\">

comes out as "<a href=" + "prop_apagesname" + ".html

target="self">". That way, I can write a link back to the

associated, chosen property as I write that chosen property into the page for

display, which happens in the 2nd line. That takes carefully canceling out the

right quotation marks, so my attributes in the tag are properly quoted.

The variable linkStr (which is constructed by pulling items out of an array

of link values) builds the opening <a> tag. Another array's value (with

the same index, 'i' as the other arrays) gets written in next, then the link

tag is closed and a line break inserted by adding"</a><br>".

This gets done for each 'true' value in the hidden inputs on the hidden frame

page. Cool, so that's how I get the list of links back to the selected properties.

But I also needed to get those values into my CGI-email form...

So, I made another array that contained the values that I want to pass to CGIemails

text template. It's just another array of strings called PageProperties. It's

values get inserted into the page like this:

<script>

<!--

for (var i = 0; i < AllProperties.length ; i++){

if (AllProperties[i].value == "true"){

var inputStr1 = "<input type=\"hidden\" value=\""

var inputStr2 = "\" name=\""

document.write(inputStr1 +

PageProperties[i] +

inputStr2 +

AllProperties[i].name +

"\">")

}

}

//-->

</script>

This is really no different than the other output loop, except that it creates

hidden input fields in the form to pass the dynamically generated values to

CGI-Email's text template. Each time through the loop, the function creates

a ' <input type="hidden" name="AllProperties[i]Name"

value="PageProperties[i]">. Then, in the text template for cgi-email,

I have a 'accommodations preferences:' field that has little [] slots for each

of the AllProperties array items...pretty simple.

Yes...it does leave a lot to be desired...

So, it's not foolproof. I had to put a function to bust the frames on all of

the pages in the site that are not in the /travel section, otherwise, if you

followed a link from the travel section to another area of the site and then

went back, you'd be in a double frame. Since I did that, if you check some properties

while you're browsing, then go look at the sail line or women's swimsuits and

come back to the travel section...your checked values are gone. And I still

have to write a function that checks the boxes if you check it once, leave the

page, and then go back. And if you have JS turned of, it doesn't work. But like

I said at the beginning of the article, it doesn't have to be a foolproof solution,

just good enough to solve the problem with a minimum investment of resources.

The client is happy because the inquiries we get now have information about

which properties the inquirer was interested in. The user is happy because they

only have to fill out one form, and can go back to the properties they checked

while filling out the form. I'm happy because I learned more about planning

and building web sites/web applications.

Thanks!