AJAX using HTML and JavaScript


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:
  1. window.document.body;
  2. document.body;
  3. self.document.body;
  4. 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