JavaScript Editor Free JavaScript Editor     JavaScript Debugger 




Main Page

Previous Page
Next Page

5.2. 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 browser. Listings 5-1, 5-2, and 5-3 show the result of loading the document in Microsoft Internet Explorer, Firefox, and Opera, respectively.

Listing 5-3. 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 in Listing 5-6?

  1. window.document.body;

  2. document.body;

  3. self.document.body;

  4. document.getElementsByTagName("body").item(0);

Listing 5-6. Sample HTML Document

<html>
  <head>
    <title>Sample</title>
  </head>
  <body>
    <p>Hello, World!</p>
  </body>
</html>

Pencils down. The correct answer is: all of them. Yes, it is a trick question, but it points out that there are many ways to reach the same destination. Think of it as an "All roads lead to Rome" thing, and no one will get hurt. Of course, it might be important to remember that some of the routes to a destination could be quicker than others.

I'd like to cover one additional, often overlooked, DOM topic. When dealing with frames, there will always be more than one #document. Not only does the frameset have a #document, but each frame will have a #document of its own.

5.2.1. JavaScript, ECMAScript, and JScript

Regardless of the name they call it by, people either love or hate JavaScript, which is probably why opinions range from it being either the greatest thing since sliced bread or the tool of the devil. Personally, I believe that cheeseburgers are the greatest thing since sliced bread and that the tool of the devil is cellphones. Nothing worse than enjoying a good cheeseburger, with onion rings on the side, and the damn phone starts playing "The Monster Mash." But I digress.

JavaScript is a tool, neither good nor bad, like any other tool; it's all in how the tool is used. Give ten people a box of tools and a job to do, and nine of them will get the job done in various degrees, while the tenth will require a call to 911. With human nature being what it is, you'll never hear about the first nine; you'll only hear about poor old Bob who did himself serious bodily harm with a router. For this reason, people will decide that routers are evil.

JavaScript essentially falls into the same category, a lightweight, interpreted object-based language, and it is extremely flexible and tightly coupled with the browser. For instance, you're now aware that by using JavaScript and DOM it is possible to modify the contents of the page as previously demonstrated without bothering the server, but are you also aware that by using JavaScript it is also possible to create objects?

Let's say, for instance, that you've got a website that uses a handful of standard-sized pop-ups. Well, rather than code them each by hand and possibly have typos on a few pages, why not create an object to open a number of standard-sized windows? Three different-sized pop-ups should suffice; add to that the capability to override the various properties, and we end up with the "function," which is really a class shown in Listing 5-7.

Listing 5-7. JavaScript childWindow Class

function childWindow(strURL, strName, strChildType) {
      /*    The purpose of this function is to act as a
       class constructor for the childWindow object.

            The properties for this object are the following:
                        url         = uniform resource locator
                        name        = child window name
                        child       = child window object
                        attributes  = child window attributes

                        The methods for this object are the following:
                        open()            = Opens and sets focus to the
                                            childWindow
                        close()           = Closes the childWindow
                        focus()           = Sets focus to the childWindow
                        closed()    = Returns a boolean indicating if the

                                                  childWindow is open.
      */
      var reName = new RegExp('[^a-z]','gi'); // Regular expression
      var e;
// Dummy for error code

// Properties
      this.url = strURL; // Uniform resource locator
      this.name = strName.toString().replace(reName,'');
      this.childType = strChildType; // Child window type
      this.child = null; // Child window object
      this.alwaysRaised = 'no'; // Window always raised
      this.copyhistory = 'yes'; // Copy browser history
      this.height = ''; // Window's height
      this.left = 0; // Window's left start position
      this.location = 'no'; // Window's location box
      this.menubar = 'no'; // Window's menu bar
      this.resizable = 'yes'; // Window's resizable
      this.scrollbars = 'yes'; // Window's scroll bars
      this.status = 'yes'; // Window's status bar
      this.toolbar = 'yes'; // Window's tool bar
      this.width = ''; // Window's width
      this.top = 0; // Window's top start position

// Methods
      this.open = childWindowOpen; // Open method
      this.close = childWindowClose; // Close method
      this.focus = childWindowFocus; // Focus method

// Determine attributes based on type
      try {
            if(typeof this.childType != 'undefined')
                  switch(this.childType.toLowerCase()) {
                        case 'info':
                              this.height = Math.round(screen.availHeight
* 0.4);
                              this.width = Math.round(screen.availWidth *
0.4);
                              this.left = (screen.availWidth -
Math.round(screen.availWidth * 0.4) - 8) / 2;
                              this.top = (screen.availHeight -
Math.round(screen.availHeight * 0.3) - 48) / 4;
                              this.toolbar = 'no';

                              break;
                        case 'help':
                              this.height = Math.round(screen.availHeight
* 0.7);
                              this.width = Math.round(screen.availWidth *
0.8);
                              this.left = screen.availWidth -
Math.round(screen.availWidth * 0.8) - 8;
                              this.top = (screen.availHeight -
Math.round(screen.availHeight * 0.7) - 48) / 4;

                              break;
                        case 'full':
                              this.height = screen.availHeight - 48;
                              this.width = screen.availWidth - 8;
                              this.toolbar = 'no';

                              break;
                        default:
                              throw(null);

                              break;
                 }
           else
                 throw(null);
     }
     catch(e) {
           this.height = screen.availHeight - 147;
           this.width = screen.availWidth - 8;
           this.menubar = 'yes';
           this.resizable = 'yes';
           this.scrollbars = 'yes';
           this.status = 'yes';
           this.toolbar = 'yes';
           this.location = 'yes';
     }

      function childWindowOpen() {
            /*    The purpose of this function is to act as the open
for the childWindow object by
opening a window with attributes based upon
               the window type specified.
            */

            var strAttributes; // Window attributes
            var e;
       // Dummy error

            // Build window attribute string
            strAttributes = 'alwaysRaised=' + this.alwaysRaised;
            strAttributes += ',copyhistory=' + this.copyhistory;

            if(typeof this.height == 'number')
                  if(this.height > 0)
                        strAttributes += ',height=' + this.height;

            strAttributes += ',left=' + this.left;
            strAttributes += ',location=' + this.location;
            strAttributes += ',menubar=' + this.menubar;
            strAttributes += ',resizable=' + this.resizable;
            strAttributes += ',scrollbars=' + this.scrollbars;
            strAttributes += ',status=' + this.status;
            strAttributes += ',toolbar=' + this.toolbar;
            strAttributes += ',top=' + this.top;

            if(typeof this.width == 'number')
                  if(this.width > 0)
                        strAttributes += ',width=' + this.width;
      // Try to open a child window
            try {
                  this.child = window.open(this.url, this.name,
strAttributes);

                  if(window.opener.name == this.name)
                        this.child = window.opener;
                  else
                        if(window.opener.opener.name == this.name)
                              this.child = window.opener.opener;
                        else
                              if(window.opener.opener.opener.name ==
this.name)
                                    this.child =
window.opener.opener.opener;
                              else
                                    if(window.opener.opener.opener.name ==
this.name)
                                          this.child =
window.opener.opener.opener;

                  this.focus();
            }
            catch (e) {
                  this.focus();
            }
      }
      function childWindowClose() {
            /*    The purpose of this function is to act as the
close method for the childWindow
               object and close the child window.
            */
            var e;
      // Dummy for error code

            try {
                  this.child.close();
            }
            catch (e) { }
      }

      function childWindowFocus() {
            /*    The purpose of this function is to act as the
focus method for the childWindow
               object. In other words, set focus to the
                  child window.
            */
            this.child.focus();
      }
}

As with the more traditional languages, to use our window object, it is necessary to instantiate the classin other words, create an instance of the class. Listing 5-8 shows how instantiation is accomplished, and Figure 5-1 displays the result.

Figure 5-1. childWindow class in action


Listing 5-8. Example of Using the childWindow Class

var child = new childWindow('child.html','child','info');
child.open();

Another often overlooked feature of JavaScript is its recursive capabilities, although, come to think of it, this might be an intentional omission. For some reason, the majority of developers avoid recursion like it's an Osmonds' or a Carpenters' album. I'm of the opinion that the reason for this is that, as with the albums from either of the two mentioned groups, recursion can cause headaches. Of course, it might be more because, unless trained, our minds don't readily lend themselves to thinking recursively.

Nevertheless, sometimes recursion is the easiest way to handle a particular coding issue. And not computing Fibonacci numbers or the factorial of a number, which are those "make work tasks" designed to keep computer science professors off the street. Group those two problems with singly- and doubly-linked lists, and they're good for a whole semester.

Instead, let's examine the transverse() function from Listing 5-2, which, for convenience, has been copied here to Listing 5-9. With the exception of the enclosed ancestor() function, the TRansverse() function is pretty much a classic example of recursion coded in JavaScript. The same can be said of the ancestor() function, whose sole purpose is to return a greater-than sign for every ancestor of the current node.

Listing 5-9. Listing 5-2 Repeated

/*
      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('');
      }
}

5.2.2. A Problem to Be Solved

With all due respect to one of my previous managers who believed that there were no such thing as problems, only opportunities, there is one problem that I've been meaning to solve for a while now. It's one of those things that the average person, one without mad scientist tendencies, doesn't realize exists. Where do mad scientists shop online? Oh, sure, there's Amazon.com and Walmart.com, but have you ever tried to purchase a cask of Amontillado, or stones and mortar from either website? These essential tools of the trade just aren't readily available online.

The big websites just don't appreciate the needs of the lonely mad scientist. In fact, it might be a good idea to include some of the other often-underrepresented groups as well. I imagine that alchemists and sorcerers have some issues shopping for the tools of their trades as well. I, for one, have never seen either site offer retorts or grimoires or anything along those lines. Not that I know what a retort is; I imagine that it is some kind of backup Linzer torte or something along those lines. There is definitely an untapped market here, so much so that, had I conceived of this idea about six years ago, it would be necessary to beat off potential investors with a stick.

I envision this website as a pretty normal series of web pages, starting with a splash page that takes the visitor to a page displaying items for the various guilds: mad scientist, alchemist, and sorcerer. The visitor would then have the option of browsing all the items available or filtering by guild.

Shoppers could view the details of the individual items and, if desired, add them to their shopping cart, which can be displayed at any time. When they were sure that they had everything they want, they could proceed to checkout, enter their shipping and billing information, and be off.

So with that idea in mind, the various web pages fall into a few simple categories:

  • Those that display tabular information that cannot be altered, such as the items for sale

  • Pages that display tabular information that can be updated, such as the quantities of items in the shopping cart

  • Static form-type pages, such as those that verify your shipping address page

  • Updateable forms, such as the page where the visitor enters the shipping billing information

Oh, and the other thing I forgot to mention: This site needs to work with a selection of different web browsers. I have a couple of totally logical reasons to require this cross-browser capability. The first reason is to appeal to as wide a customer base as possible because the more customers, the more sales. The second is, it might not be a good idea to tick off someone who is potentially creating a Moon-Mounted Death Ray. Hmm, note to self: Use a P.O. Box as a corporate address.

Before proceeding any further, now is a good time to delve a little into the server-side environment. Let's start with the operating system and web server; I'm using Windows XP Professional and Internet Information Server. The reason for this is the usual: It came on the machine, and I'm too lazy to change it. Besides, I'm pretty sure that "Age of Mythology" doesn't run on Linux. Note to self: Make sure that you don't get caught by Mary Ann playing when you should be writing.

So far, my environmental choices have been pretty boring, and the open source people are thinking that Firefox alone doesn't cut it for a book. Alright, how about MySQL version 5? In fact how, about MySQL version 5 with stored procedures? Interested? Well, then, read on.

In version 5, MySQL introduced a feature that had been in the proprietary databases for quite some time: stored procedures. Just in case you were abducted by aliens in 1974 and only recently got back to Earth, let me explain what stored procedures are. Stored procedures are preparsed SQL that accepts parameters and can return results.

Let's say, for example, that we have a table consisting of the states and territories of the United States and the provinces of Canada. Let's also say that we'd like the option of passing the procedure a two-character abbreviation to receive the name of the state or province, or passing a null value to obtain the names and abbreviations of all. We would create a stored procedure that looks a lot like the one shown in Listing 5-10.

Listing 5-10. A MySQL Stored Procedure

DELIMITER $$

DROP PROCEDURE IF EXISTS 'ajax'.'stateSelect'$$
CREATE PROCEDURE 'ajax'.'stateSelect'(
  stateAbbreviation VARCHAR(2)
)
BEGIN
  SELECT  state_abbreviation,
          state_name
  FROM    state
  WHERE   (stateAbbreviation IS NULL OR stateAbbreviation =
state_abbreviation);
END$$

DELIMITER ;

Now that we have a stored procedure, the big question is, what do we do with it? Fortunately, that's an easy question; we call it as shown in the first example here. However, I'd like to point out that because of the way the stored procedure is called, when a parameter is null, a null must, in fact, be passed as shown in the second example.

CALL stateSelect('NJ');

CALL stateSelect(NULL);

Now that the database issue is out of the way, it is time to figure out what to code the server side in. My first thought was to pick a language that has a proven track record and was widely accepted, but I could not find a reliable source of punch cards, so COBOL wasn't a viable option. The really scary part is that I've seen it attempted at companies because they thought that they could port their mainframe CICS code to the Web, but that is another story.

I finally decided on PHP 5. My reasons for this are several. The first is that I've seen it and know that, not only does it work, but it works well. Another reason is that it appears to be a combination of C and UNIX Shell, both of which I've worked with in the past. The third reason is that it plays well with MySQL and stored proceduresat least, once configured correctly and if I remember to use the mysqli library instead of the older mysql library.

The final reason is that it is open source, and, therefore, several slick IDEs such as PHP Designer 2005 from MPSOFTWARE are available to those of us on limited budgets.


Previous Page
Next Page

Bitcoin Dice - Crypto Casino . Google mobile rank checker.


JavaScript Editor Free JavaScript Editor     JavaScript Debugger


©