Ajax Using HTML and JavaScript
Human beings, as well as other life
forms, are made up of chemicals such as iron, nitrogen, and water. However,
simply mixing everything together in a cauldron and giving it a quick stir won't
result in someone climbing out of the cauldron. The reason for this is that it
isn't the type of ingredients put together; it is how the ingredients are put
together. After all, if girls really were made of sugar and spice and everything
nice, there would be a lot more geeky guys with dates at the prom. If you've
ever read Lester Del Rey's short story Helen
O'Loy, you might be accustomed to the concept of building a date from
things lying about.
The same is true for web applications. Consider for a moment
what is commonly referred to as Dynamic HTML, or DHTML, for short. Still
commonly used in web applications, it is distinguished from plain HTML only by
the fact that things happened based upon events. This is where the dynamic part comes in. I would like to point out that
at no time did I mention the word JavaScript. The
reason for this is that not only is it possible to have DHTML without
JavaScript, but it is also possible to have JavaScript without DHTML.
Older Frame
<frameset rows="100%,*"> <frame name="visible_frame" src="visible.htm"> <frame name="hidden_frame" src="hidden.htm"> <noframes> Frames are required to use this web site. </noframes> </frameset> |
As mentioned in the previous chapter, the
rows="100%,*" performs the magic, but it isn't the only method
available to us. In fact, looking at only the opening frameset tag, the following eight examples all
produce the desired results:
<frameset rows="100%,*"> <frameset rows="100%,0"> <frameset rows="*,0%"> <frameset rows="*,0"> <frameset cols="100%,*"> <frameset cols="100%,0"> <frameset cols="*,0%"> <frameset cols="*,0">
The reason for this plethora of choices is that this is one of
those times when we really don't care how the hiding is accomplishedall that
matters is that the hiding is accomplished. Oh,
this is a good time for me to point out that when developing a new application
using hidden frames, it isn't a violation of the mad scientist rules to make the
hidden frame visible for testing. It is, however, a violation to let others see
the frame with the hidden frame visible, both because it gives the impression
that something is wrong with our fiendish plans and because it looks ugly.
Unlike framesets, in which the hiding is accomplished through
the use of either rows or columns, iframes have the much-easier-to-remember
height and width attributes, as the following tag shows:
<iframe height="0" width="0" src="hidden.htm">
That's itjust the one measly little tag, and we've got
something that kind of looks a lot like Ajax. Right about now you're either
taking my name in vain or wondering why I didn't start with iframes. In fact,
there are probably some out there who are doing both. Well, the answer is both
personal and simple. Whenever I learn something new, I try to immerse myself
totally in it, avoiding all shortcuts until whatever I learned becomes second
nature. To be totally honest, after learning to swim, I was wrinkled for a
week.
Cross-Browser DOM
Now that we have either classic frames or iframes, we have
reached one of the most widespread reasons for their avoidance: the matter of
access. Short of a crystal ball and tea leaves, or maybe two soup cans and a
piece of string, just how do the
various frames communicate? I've worked with some web developers who believed
that it was easier to talk with the ghost of Elvis than to have individual
frames communicate with one another. However, to be honest, most of those web
developers talked of black helicopters and wore aluminum foil hats to ward off
mind control.
As much as it seems otherwise, interframe communications is
relatively simple and can be dealt with using one word: DOM. Alright, you caught me in a fib; DOM is an
acronym, so it's really three words, Document Object Model. Coming in both HTML
and XML flavors, in this instance, the DOM is a hierarchical representation of a
web page that allows JavaScript to access and modify a page. Actually, careless
coding when using the DOM is a most excellent way for a page to self-destruct, a
la "Good morning, Mister Phelps."
As formidable as the DOM sounds, it is nothing more than a
hierarchical representation of a document, which, in this case, is an HTML
document. Think treesthe data structure trees, not the green woody things. And,
no, not binary trees; we want the ones that can have more than two children.
Just in case you need a little refresher in the structure of
trees, it goes like this:
-
Each of the tags in an HTML document can be referred to as a node or element.
-
There is only one topmost node, which is called the root node.
-
All nodes are descendants of the root node, either directly or indirectly.
-
With the exception of the root node, all nodes have a single parent node.
-
Nodes that occur on the same tree level that share a parent are called siblings.
-
The immediate descendants of a particular node are referred to as that node's children.
However, you must remember one thing when accessing the
Document Object Model: Here be monsters. This is one of those places where it is
really necessary to test things on several different browsers. The reason for
this is the usual; it is basically a question of interpretation of the World
Wide Web Consortium's DOM specifications. This might sound a little like the
schisms that occur between different sects of the same religion, but depending
on the application, it can cause some major headaches. Listing 5-2 shows an example of this potential
problem.
Listing 5-2. Example of a Problem Created by Differing Interpretations of the W3C's DOM Specs
<html> <head> <title>DOM Test</title> <script language="JavaScript"> /* Recursively transverse the HTML DOM using the passed node as a starting point. */ function transverse(obj) { var strNode = ancestor(obj) + obj.nodeName.toString() + '\n'; for(var i=0;i < obj.childNodes.length;i++) strNode += transverse(obj.childNodes.item(i)); return(strNode); function ancestor(obj) { if(obj.parentNode != null) return('>' + ancestor(obj.parentNode)); else return(''); } } </script> </head> <body onload="document.getElementById('textarea1').value = transverse(document)"> <table width="300" border="1" cellspacing="1" cellpadding="1"> <tr> <td> <input type="text" id="input1" name="input1" /> </td> </tr> <tr> <td> <textarea id="textarea1" name="textarea1" cols="80" rows="20"></textarea> </td> </tr> </table> </body> </html> |
Consisting of an HTML document with an
embedded JavaScript function whose sole purpose is to transverse the document,
the page just shown yields some interesting results, depending on the web
browsers. show the result of loading the document in Microsoft
Internet Explorer, Firefox, and Opera, respectively.
Microsoft Internet Explorer
#document >HTML >>HEAD >>>TITLE >>>SCRIPT >>BODY >>>TABLE >>>>TBODY >>>>>TR >>>>>>TD >>>>>>>INPUT >>>>>>>#text >>>>>TR >>>>>>TD >>>>>>>TEXTAREA >>>>>>>>#text >>>>>>>#text |
Listing 5-4. Firefox
#document >HTML >>HEAD >>>TITLE >>>>#text >>>#text >>>SCRIPT >>>>#text >>#text >>BODY >>>#text >>>TABLE >>>>#text >>>>TBODY >>>>>TR >>>>>>#text >>>>>>TD >>>>>>>#text >>>>>>>INPUT >>>>>>>#text >>>>>>#text >>>>>#text >>>>>TR >>>>>>#text >>>>>>TD >>>>>>>#text >>>>>>>TEXTAREA >>>>>>>#text >>>>>>#text >>>>>#text >>>#text |
Listing 5-5. Opera
#document >HTML >>HEAD >>>TITLE >>>>#text >>>SCRIPT >>BODY >>>#text >>>TABLE >>>>TBODY >>>>>TR >>>>>>TD >>>>>>>#text >>>>>>>INPUT >>>>>>>#text >>>>>TR >>>>>>TD >>>>>>>#text >>>>>>>TEXTAREA >>>>>>>>#text >>>>>>>#text >>>#text >>>#text >>>#text |
Interesting, isn't it? You can't even play the Sesame Street "One of these things ain't like the
other" song because none of them is like the others. However, more similarities
exist than differences, such as the basic structure and the existence of
specific nodes. What is important to remember is that, depending on the web
browser, #TEXT elements can be sprinkled here and there.
Now that this is out of the way, let's take a closer look at
the HTML document in Listing 5-6, with
the goal of locating specific elements, such as the BODY element. As a
matter of fact, grab a number 2 pencil; it's time for a pop quiz. Which of the
following JavaScript statements can be used to locate the BODY element
in the HTML document shown below:
-
window.document.body;
-
document.body;
-
self.document.body;
-
document.getElementsByTagName("body").item(0);
Sample HTML Document
<html> <head> <title>Sample</title> </head> <body> <p>Hello, World!</p> </body> </html> |
Frameset
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>MSAWSS</title> </head> <frameset rows="100%,*"> <frame name="visible_frame" src="visible.html"> <frame name="hidden_frame" src="customer.php?email=ewoychowsky@yahoo.com"> <noframes> Frames are required to use this web site. </noframes> </frameset> </html> |
But before adding the necessary logic,
let's see what JavaScript functions we already have that can be cloned for our
nefarious purpose. The first JavaScript function to be cloned is
changeEvent, which itself does a little cloning. The sole purpose of
this little cross-browser-capable function is to handle an onchange
event for HTML input, textarea, and select tags. The
second function that can be cloned is submitForm, which, surprisingly,
is also cross-browser-capable.
At this point in designing the architecture, I have run out of
code to clone and now must write code from scratch. But before I do, allow me to
explain what I'd like to do. After all, explaining plots is a common weakness
that we mad scientists all have, and if I can't explain it to you, I'll have to
explain it to Igor, and the blank, glassy stare that he gets is so
unnerving.
First I'd like a routine that ensures that the peasantseh,
guestsdon't muck around with the Back button. This is because the Back button is
like fire to Victor's monsterit causes unpredictable results. With any kind of
HTML frames, hitting the Back button is just as likely to cause the hidden page
to go back as the visible page. In short, it is not a good thing. Fortunately,
in this instance, a little JavaScript goes a long way, as the following line of
code shows:
window.history.forward(1);
Doesn't look like much, does it? Well, it isn't the size of the
boat, but the, um, never mind. Let's just say that it is all that is necessary
to ensure that the current page is always the top page in the history, which is
exactly what this does. Of course, it needs to be included on every page, both
visible and hidden. It is also important to remember to provide some means of
navigation; otherwise, shoppers will be lost in a "twisty little maze of
passages, all alike," which isn't real good for repeat business.
The next function isn't really a function at all; it is
actually a Boolean global variable that merely indicates whether the web browser
is Microsoft Internet Explorer or another browser. The reason this is an
Internet Explorer indicator isn't because I'm in love with IE; it is because the
larger the software company is, the more likely that it has wandered off the
path when it comes to following standards. So with this in mind, the following
code was written:
var _IE = (new RegExp('internet explorer','gi')).test(navigator.appName);
The third function that is necessary to this project is one
that "clones" a form on the hidden frame to the visible. Although this sounds
pretty simple, it is anything but simple. In fact, most developers never ask one
major question unless they try this kind of thing for themselves:
When loading the frameset for the first time, which page loads
first?
Fortunately, there is a simple answer to this question;
unfortunately, the answer is that I don't know, which is a rather big stumbling
block to overcome to complete the website. This means that not only will the
function need to clone the hidden form to the visible form, but it might have to
sit around waiting for the visible form to finish loading. The good thing is
that the process of checking for frame completeness is very similar to what was
done in previous chapters and as shown in
initialize Function
/* Update the visible frame with information from this page. */ function initialize() { var hiddenForm = document.getElementById('hidden_form'); if(_IE) { if(parent.document.frames.item('visible_frame').document.readyState != 'complete') window.setTimeout('initialize()',100); else parent.frames['visible_frame'].document.getElementById('visible_form'). innerHTML = hiddenForm.innerHTML; } else { try { var node = parent.frames['visible_frame'].document.getElementById('visible_form'). firstChild; try { parent.frames['visible_frame'].document.getElementById('visible_form'). removeChild(node); } catch(e) { } parent.frames['visible_frame'].document.getElementById('visible_form'). appendChild(hiddenForm.cloneNode(true)); } catch(e) { window.setTimeout('initialize()',100); } } } |
The initialize() function is invoked by the hidden
frame's onload event handler, and the first thing that it does is use
the _IE Boolean that I created earlier. The reason for this is that
occasionally I do give in to temptation and use a nonstandard browser feature.
In this instance, the feature is the document object's readyState
property. Just test it against "complete," and we're good to go (that is, if the
browser is Microsoft Internet Explorer; otherwise, it is necessary to give it
the old college try and catch).
If the visible frame isn't ready, it is necessary to use the
window.setTimeout() method to invoke the initialize() function
again after waiting the specified number of milliseconds. Don't confuse this
method with the window. setInterval() method because
setTimeout invokes the specified function only once. With
setInterval(), the function repeats like salami does until it is
stopped, which is bad, unless you are fond of debugging really weird client-side
happenings.
The next function that I want to add is one to restrict
keyboard input to numeric values. Although the appropriate elements can be
tested at submission time, we're dealing with guests who could potentially
unleash a plague of giant hedgehogs on Spotswood, New Jersey, when ticked off.
So why not avoid any problems before they occur? Listing 5-13 shows this function in all its glory.
Listing 5-13. restrict Function
/* Restrict keyboard input for the provided object using the passed regular expression and option. */ function restrict(obj,rex,opt) { var re = new RegExp(rex,opt); var chr = obj.value.substr(obj.value.length - 1); if(!re.test(chr)) { var reChr = new RegExp(chr,opt); obj.value = obj.value.replace(reChr,''); } } |
changeEvent and submitForm Functions
/* Handle form visible form onchange events. Values from the visible form are copied to the hidden form. */ function changeEvent(obj) { parent.frames[1].document.getElementById(obj.id).value = obj.value; } /* Submits the form in the hidden frame. */ function submitForm() { parent.frames[1].document.getElementById('hidden_form').submit(); } </script> </head> <body onload="initialize()"> <form name="visible_form" id="visible_form"></form> </body> </html> |
SQL to Create MySQL Database Tables
CREATE TABLE guild ( guild_id int(6) auto_increment NOT NULL, guild_name varchar(255) NOT NULL, PRIMARY KEY (guild_id), UNIQUE id (guild_id) ); CREATE TABLE orders ( orders_id int(6) auto_increment NOT NULL, customer_id int(6) NULL, ship_address_id int(6) NULL, orders_date datetime NOT NULL, PRIMARY KEY (orders_id), UNIQUE id (orders_id), KEY customer_key (customer_id), KEY ship_address_key (ship_address_id) ); CREATE TABLE item ( item_id int(6) auto_increment NOT NULL, item_name varchar(255) NOT NULL, item_description varchar(255) NULL, item_price decimal(10,2) NOT NULL, PRIMARY KEY (item_id), UNIQUE id (item_id) ); CREATE TABLE line ( line_id int(6) auto_increment NOT NULL, orders_id int(6) NOT NULL, item_id int(6) NOT NULL, line_quantity int NOT NULL, line_item_price decimal(10,2) NOT NULL, PRIMARY KEY (line_id), UNIQUE id (line_id), KEY orders_key (orders_id), KEY item_key (item_id) ); |
Next Chapter
No comments:
Post a Comment