Bristle Software JavaScript Tips

This page is offered as a service of Bristle Software, Inc.  New tips are sent to an associated mailing list when they are posted here.  Please send comments, corrections, any tips you'd like to contribute, or requests to be added to the mailing list, to tips@bristle.com.

Table of Contents:

  1. Getting Started
    1. JavaScript Intro
    2. Popup Messages
    3. JavaScript Variables
    4. HTML Syntax
      1. HTML <script> Tag
        1. HTML <script> Tag With Inline JavaScript Code
        2. HTML <script> Tag To Load a Separate JavaScript File
    5. JavaScript Namespaces
      1. Creating a namespace
  2. DHTML
    1. DHTML Intro
    2. Getting an HTML element to operate on
    3. Name vs. Id
    4. Getting dynamic style of an HTML element
    5. Hiding/showing HTML elements
    6. Enabling/disabling HTML elements
    7. "Fake disabling" HTML elements
    8. Bolding/unbolding HTML text
    9. Setting color
  3. DOM Events
    1. Handling Events
      1. Handling Events via HTML Attributes
      2. Handling Events via DOM Properties
    2. Getting the Event keyCode
  4. Controlling the Browser
    1. Disabling the Backspace key
    2. Showing a temporary statusbar message
  5. Ajax
    1. Ajax Intro
      1. What is Ajax?
      2. Why use Ajax?
      3. How Much to use Ajax?
      4. See Also
    2. Simple Ajax Example
    3. XMLHttpRequest Methods
    4. XMLHttpRequest Properties
    5. Bristle Ajax Library
      1. Create an Ajax Object
      2. Register Ajax Callbacks
      3. Get XML via Ajax
      4. Synchronous Ajax
    6. Loading Images
      1. Set the SRC property of the IMG
      2. Prevent Browser Image Caching
      3. Beware <IMG SRC=''>
    7. Using Ajax for a Keep-Alive
    8. Using Ajax for Drill-down (Coming soon...)
    9. Using Ajax for Infinite Scrolling of Long Tables (Coming soon...)
    10. Using Ajax for Data Validation (Coming soon...)
    11. Comet: Using Ajax for Server Push (Coming soon...)
  6. RWD -- Responsive Web Design
  7. SPA -- Single Page App
  8. PWA -- Progressive Web App
  9. Algorithms
    1. Generating random numbers (coming soon...)
    2. Generating unique variable names (coming soon...)
  10. Tools
    1. Debugging Tools
      1. View Source
      2. View Source of Referenced JavaScript and CSS Files
      3. Firefox View Live Source
      4. Firefox DOM Inspector (coming soon...)
      5. Firefox Developer Kit (coming soon...)
      6. Venkman JavaScript Debugger (coming soon...)
      7. Firebug JavaScript Debugger (coming soon...)
      8. Microsoft Script Debugger (coming soon...)
      9. Bristle BrowserWidth tool
      10. Bristle JavaScriptEnabled tool
      11. Bristle Color Picker
    2. Code Generation Tools
      1. Compass and Sass
      2. CoffeeScript (coming soon...)
    3. Testing Tools
      1. SauceLabs.com
      2. DEV, TEST and PROD Bookmarklets
  11. HTML5
    1. Intro to HTML5 (coming soon...)
    2. HTML5 "form" attribute of various input fields
    3. HTML5 Device APIs
  12. Pjax
    1. Intro to Pjax
  13. Mobile Web
    1. Intro to Mobile Web (coming soon...)
    2. Responsive Web Design (coming soon...)
  14. CMS -- Content Management Systems
    1. Intro to CMS Systems
  15. Dart -- Google's JavaScript replacement
  16. See Also

Details of Tips:

  1. Getting Started

    1. JavaScript Intro

      Original Version: 4/14/2007
      Last Updated: 4/30/2007
      Applies to:  JavaScript 1.0+

      JavaScript (more properly called ECMAScript) is not Java.  However, for their simpler features, they both use the same syntax as C, C++, Perl and many other languages, all based originally on the syntax of the C language.  For many of the basic programming constructs, it is hard to tell which language you are looking at:

              // One-line comment
              /* Block comment */
              if (x==y) {doSomething(); } else { doSomethingElse(); }
              for (i=1; i < 10; i++) { doSomething(); } 
              while (i < 10) { doSomething(); }

      However, the similarities end there.  Java is a "strongly typed" and "statically typed" language where the compiler catches many simple programmer mistakes by requiring an object to be declared with a data type and to maintain that same data type at all times.  It is very good for writing large scale mission-critical systems.  JavaScript is a "dynamically typed" scripting language where you don't declare the type of an object, and its type can change on the fly.  You can even add new properties and methods to an object on the fly.  Very good for writing small scale non-critical bells and whistles to run in a Web page, as part of the user interface to a Java application.

      You don't need to download and install a compiler or runtime environment for JavaScript.  There is a JavaScript interpreter built into every Web browser.  Therefore, JavaScript is commonly used for logic written to run in the browser for a Web application, including the browser-side logic for "Ajax" interactions (more on that later).

      For more details, see the JavaScript and ECMAScript links on my links page:

          http://bristle.com/~fred/#javascript

      --Fred

    2. Popup Messages

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      The simplest way to prompt the user in JavaScript is to pop up a message and wait for the user's response.  There are 3 standard JavaScript methods to do so: 

      alert (message) Pop up a message with an OK button.  Wait for OK to be clicked.
      confirm (message) Pop up a message with OK and Cancel buttons.  Wait for a button to be clicked.  Return true if OK is clicked; false if Cancel.
      prompt (message, [default]) Pop up a message and a text input field with OK and Cancel buttons.  If a default value was specified, initialize the input field with it.  Otherwise, leave the input field empty.  Wait for the user to optionally change the value of the input field, and then click a button.  Return the value of the input field if OK is clicked; null if Cancel.

      These are the simplest ways to prompt the user.  However, they have a number of limitations:

      1. They do not support HTML messages.
        The message string must be a simple JavaScript string of text.  HTML elements are not rendered, so you cannot specify fonts, colors, sizes, styles, background color, images, tables, etc.  However, you CAN embed newline chars in the string as:  "line 1\nline2\nline3", except for the Internet Explorer version of prompt() which chops it off at the 2nd newline.
         
      2. They are synchronous.
        The next line of JavaScript does not execute until the user clicks OK or Cancel.  Therefore the entire thread is held up waiting for a response.
         
      3. They are modal.
        The user cannot ignore the popup and continue interacting with the window under it.  He must respond to the popup first.  Therefore the entire window, and perhaps the entire application is locked until he responds.
         
      4. They are not copyable.
        The user cannot copy and paste the message into a document, a support log, or a mail message to report the error to a support team
         
      5. They require dismissal.
        The messages do not go away if the user ignores them or moves the mouse away.  They must be explicitly dismissed by clicking a button (or by hitting Enter for OK or Esc for Cancel).  Also, you cannot write JavaScript code to dismiss them automatically (after a timeout, or after some user action, for example).
         
      6. They are not scrollable
        The message string is displayed without a scrollbar, so long messages fall off the bottom of the screen.  Furthermore, since the buttons appear below the text of the message, long messages push the buttons off the bottom of the screen so the user cannot dismiss the message with the mouse and has to use the Enter or Esc key.
         
      7. They are immutable
        The message string cannot be changed, or more messages appended to the same popup, after the popup has occurred.  Sometimes it is useful to have a single popup that shows a series of messages, with more messages being added as the user reviews earlier messages.
         
      8. They may interfere with other events.
        Because of the "nested modal event loop" used to implement these methods, you may find that they interfere with other events.  For example, a timer event may be lost while the popup is waiting for a user response.  Or an asynchronous Ajax event may be lost.

      These are good for debugging (other than issue #8 above, which can be a problem when trying to track down a bug), and for demos, but not so good for production code, where it is often better to pop up a message that is HTML, asynchronous, non-modal, copyable, able to be dismissed programmatically, scrollable, changeable, and doesn't interfere with other events.  In a future tip, I'll describe how.  

      Meanwhile, for a transient message that does not require dismissal, you may prefer to use the "title" attribute of an HTML element instead.  It appears automatically when the user hovers the mouse over the HTML element and disappears automatically when the user moves the mouse or hits a key.  Or write JavaScript code to respond to events like onmouseover, onmouseout, onkeyup, etc.

      For more info on alert(), confirm(), prompt() and other standard JavaScript functions, see:

          http://www.javascriptkit.com/jsref/globalfunctions.shtml

      --Fred

    3. JavaScript Variables

      Original Version: 4/14/2007
      Last Updated: 5/4/2007
      Applies to:  JavaScript 1.0+

      As in most scripting languages, JavaScript variables are dynamically typed.  When you declare a variable, you don't give it a static type.  Instead, you simply declare it and/or initialize it.  You must do one or both of these before referring to its value.  Thus:

              var x = 1;      // Declares x with a numeric value of 1.
              alert(x);       // Shows:  1
      
              y = "hello";    // Initializes (and implicitly declares) y 
                              // with a string value of "hello".
              alert(y);       // Shows:  hello
      
              var z;          // Declares z with a special undefined value.
              alert(z);       // Shows:  undefined
      
              z = y + y;      // Assigns z the string value "hellohello".
              alert(z);       // Shows:  hellohello
      
              z = x + x;      // Assigns z the numeric value 2.
              alert(z);       // Shows:  2
      
              x = "a string"; // Assigns x the string value "a string", 
                              // changing its type from numeric to string.
              alert(x);       // Shows:  a string
      
              z = x + x;      // Assigns z the string value "a stringa string"
              alert(z);       // Shows:  a stringa string
      
              var z;          // Re-declares z, leaving its value intact.
              alert(z);       // Shows:  hellohello
      
              alert(zz);      // Throws an exception, since zz does not exist.

      --Fred

    4. HTML Syntax

      1. HTML <script> Tag

        1. HTML <script> Tag With Inline JavaScript Code

          Last Updated: 4/14/2007
          Applies to:  JavaScript 1.0+

          JavaScript is most commonly used inside HTML pages, where it is wrapped in an HTML <script> element.  

          The <script> element can contain the actual JavaScript code, as:

                  <script language='JavaScript'>
                    <!-- Hide script from older browsers, as HTML comments.
          
                    // Global code that is executed as soon as it is found.
                    var i = 1;
                    alert("i = " + i);
                    // Function to do something useful.  Not executed until called.
                    function doSomething(evt, obj)
                    {
                      var i = 2;
                      alert("i = " + i);
                      return false;
                    }
                    //-->
                  </script>

          Note the HTML comment delimiters ("<!--" and "-->") used inside the <script> tag, but outside the JavaScript code.  This is for compatibility with really old browsers (1990's-era) that don't recognize the <script> tag.  Browsers typically ignore tags they don't recognize, but may still render the contents of the tag, so this prevents the JavaScript code from being shown to the user in a really old browser.

          Also, note the JavaScript comment delimiter ("//") before the HTML close comment delimiter ("-->").  This prevents the JavaScript interpreter from complaining about "-->" being invalid JavaScript code.

          --Fred

        2. HTML <script> Tag to Load a Separate JavaScript File

          Last Updated: 4/14/2007
          Applies to:  JavaScript 1.0+

          Instead of containing the JavaScript code, a <script> tag can mention the name of a separate file that contains the JavaScript code, as:

                  <script language='JavaScript' src='js/sourcefile.js'></script>

          The file is specified using a pathname relative to the current HTML filename, so the above would look in the "js" subfolder of the folder containing the HTML file.

          I don't recommend using the shorter HTML format (ending the start tag with "/>" and using no explicit end tag), because I've had trouble with it in some browsers:

                  <script language='JavaScript' src='js/sourcefile.js' />

          Since the default language is 'JavaScript', you can omit the language attribute, as:

                  <script src='js/sourcefile.js'></script>

          You can specify other scripting languages that are supported by some browsers:

                  <script language='PerlScript' src='perl/sourcefile.pl'></script>
                  <script language='VBScript' src='vb/sourcefile.bas'></script>
                  <script language='JScript' src='js/sourcefile.js'></script>

          You can even mention the explicit version of the scripting language, which may be necessary in scripting languages where different versions are not backward compatible, or where you are using the very latest features that only the latest version supports.  I've never used it, but I think the syntax is something like:

                  <script language='JScript 1.0' src='js/sourcefile.js'></script>
                  <script language='JScript 1.1' src='js/sourcefile.js'></script>

          Personally, I always specify the language explicitly, but leave out the version number.  I've had no problems yet with incompatibility between different versions of JavaScript.  Different versions of the DHTML object model, yes, but not different versions of JavaScript.

          Finally, there is a newer syntax, where the "language" attribute is deprecated in favor of a new "type" attribute that takes the standard MIME-type syntax, as:

                  <script type='text/javascript' src='js/sourcefile.js'></script>
                  <script type='text/ecmascript' src='js/sourcefile.js'></script>

          I haven't moved to that yet because the old syntax works fine in all browsers, but the new syntax doesn't work in old browsers.

          --Fred

    5. JavaScript Namespaces

      1. Creating a namespace

        Last Updated: 4/14/2007
        Applies to:  JavaScript 1.0+

        The problem:

        How to avoid name collisions in JavaScript?

        When you use only a couple of JavaScript functions and variables in a Web page, it is easy to avoid name collisions.  However, as you make more use of JavaScript, you start to accumulate a lot of functions and variables, and need naming conventions.  If you have multiple people writing them, conventions become even more important.  Then, as you start using JavaScript libraries written by other people, or even other companies or vendors, it becomes important that the naming convention be standardized around the world.  Even more so if you write your own JavaScript library and want it to work in a variety of environments.

        Other languages solve this problem via "namespaces" or "packages".  What to do in JavaScript, which doesn't offer these features?

        The solution:

        You can create the effect of a "namespace" in JavaScript by taking advantage of its dynamic nature to add all of your functions and variables to JavaScript objects.  Furthermore, you can mimic the Java naming convention (based on registered Internet domain names) to avoid collisions between unrelated people and organizations throughout the world.  Here's how.

        Since I own the "bristle.com" domain, the Java convention says that I should place all of my Java classes in packages with names that start with "com.bristle" like:

            com.bristle.javalib.util.StrUtil
            com.bristle.javalib.util.GetOpt
            com.bristle.javalib.log.Logger
            com.bristle.javaapps.scalejpg.ScaleJPG
            com.bristle.webapps.slideshow.SendImageServlet

        Since no one else in the world owns "bristle.com", there should be no collisions with anyone outside my organization.

        I do the same with my JavaScript library.  Instead of polluting the global namespace with functions like: 

                function doSomethingUseful(param1, param2)
                {
                    // Put useful code here.
                }

        I write:

                com.bristle.jslib.Util.doSomethingUseful = function(param1, param2)
                {
                    // Put useful code here.
                }

        This function can be called as:

                com.bristle.jslib.Util.doSomethingUseful(1, 2); 

        In the JavaScript source file, before I declare the function, I create the nested set of objects that will hold the function, as:

                var com = {};
                com.bristle = {};
                com.bristle.jslib = {};
                com.bristle.jslib.Util = {};        

        OK.  So far, so good.  But, what if someone else does the same, and also creates the starting "com" variable, as:

                var com = {};
                com.other = {};

        Their "com" object replaces my "com" object, and all of the "com.bristle..." stuff vanishes.  Not good.  The solution is for all of us to test the existence of the "com" object before creating it.  Therefore, I actually write the following:

                if (typeof(com) == "undefined") { eval("var com = {};"); }

        Furthermore, since my JavaScript library contains lots of different groups of functions, in lots of different files, with names like:

            com.bristle.jslib.Util.js
            com.bristle.jslib.Event.js
            com.bristle.jslib.Validate.js

        I start each file with the conditional creation of all of the required nested objects, plus the unconditional creation of the most nested object that will hold all the functions and variables in that file.  For example:

                if (typeof(com) == "undefined")               { eval("var com = {};"); } 
                if (typeof(com.bristle) == "undefined")       { eval("com.bristle = {};"); } 
                if (typeof(com.bristle.jslib) == "undefined") { eval("com.bristle.jslib = {};"); } 
                com.bristle.jslib.Util = {};

        I suggest you do the same, storing your own JavaScript functions and variables in nested objects named for domains you own. 

        --Fred

  2. DHTML

    1. DHTML Intro

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      Dynamic HTML (DHTML) is a technique that allows you to change the HTML in a Web page on the fly, after it has been sent from the Web server to the browser.  You still write the HTML as before, however, you add "id" attributes to those HTML elements that you want to manipulate dynamically, as:

              <table>
                <tr>
                  <td>abc</td>
                  <td id='tdACellICareAbout'>def</td>
                  <td>ghi</td>
                  <td id='tdAnotherCellICareAbout'>jkl</td>
                </tr>
              </table>

      Then you can manipulate the HTML elements from JavaScript code using the ids. For example:

              <script language='JavaScript'>
                alert(document.all.tdACellICareAbout.innerHTML);        // Shows def
                alert(document.all.tdAnotherCellICareAbout.innerHTML);  // Shows jkl
                document.all.tdACellICareAbout.innerHTML = '<b>xyz</b>';// Changes the cell to xyz (bold)
                document.all.tdAnotherCellICareAbout.innerHTML = 'pdq'; // Changes the cell to pdq
              </script>

      Whether or not you use DHTML, browsers create a Document Object Model (DOM) in memory to represent the HTML that was loaded into the page.  Browsers do this for every Web page, regardless of whether you specify any id attributes.  The DOM for the above example would consist of a "document" object, containing a "body" object, containing a "table" object, containing a "tr" object, containing 4 "td" objects, 2 of which happen to have ids.  

      The purpose of the id attributes is to make it easier for your JavaScript code to find specific DHTML DOM objects in this potentially huge hierarchy.  The syntax "document.all.tdACellICareAbout" tells the browser to find the HTML element with id "tdACellICareAbout" anywhere in the document.  The "innerHTML" property is a property of a DOM "td" object.  Once you've found the right "td" object, you can set its innerHTML to any string of text, even strings that contain more HTML element tags, and it will update the screen immediately.  In the example above, I replace the text of a cell with bolded text, but I could just as easily have replaced the text of the cell with an entire new table nested inside that cell. For more info about HTML elements and attributes, and their corresponding DHTML objects and properties, see:

          http://bristle.com/~fred/#html
          http://bristle.com/~fred/#dhtml

      --Fred

    2. Getting an HTML element to operate on

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      As shown in the above example, the syntax:

              document.all.myId

      is a way to search for an HTML element with an id value of "myId".  This is a convenient shortcut, but not really the official way to do it.  It is supported in Microsoft Internet Explorer, Mozilla Firefox, and perhaps other browsers, but not necessarily all browsers.  The official syntax is:

              document.getElementById("myId")

      However, the shortcut is gaining popularity, and will probably soon be supported by all browsers.  

      Another variation on the shortcut is:

              document.all["myId"]

      This form and the official syntax have one advantage over the simpler dotted form, which is that the id is specified as a string value, not as an implicit property name of the "all" property.  Since it is a regular string, you can manipulate the string, computing an id value, or even reading it from a file or something, before using it to get an HTML element.  For example:

              var intCounter = 1;
              strId = "myId" + intCounter;
              alert(document.all[strId].innerHTML);

      If you changed the last line to:

              alert(document.all.strId.innerHTML);

      you'd be trying to access the HTML element with id "strId", not "myId1".

      --Fred

    3. Name vs. Id

      Original Version: 4/17/2007
      Last Updated: 5/22/2007
      Applies to:  JavaScript 1.0+

      Don't confuse the "name" attribute and the "id" attribute.

      The "id" attribute of an HTML element is used in JavaScript code to refer to the element programmatically.  It is useful only when manipulating the HTML element as DHTML.

      The "name" attribute of an HTML element is different.  If an input-type element (input, textarea, select, etc.) has a name attribute, and is inside a form, then its name/value pair gets sent to the server when the form is submitted.  The name attribute predates the existence of DHTML and is useful in any HTML data entry page, regardless of whether DHTML and JavaScript are being used.

      These are two very different things.  However, lots of people confuse them, so browsers try to be helpful and accommodate the wrong one being used in some situations, so more people confuse them, etc.  You can get away with using the wrong one sometimes, but it is better to use them correctly.  Add an id to all elements you want to manipulate by id in JavaScript.  Add a name only when the element is supposed to send data to the server.

      When choosing ids and names for your HTML elements, keep in mind the different purposes that they serve.  One convention is to use the same value for name and id, but I don't think that is appropriate because it just adds to the confusion between the two.  

      Instead, I set the id to reflect the type of HTML control and other things known about it by the local JavaScript that manipulates it as an HTML element, and I set the name to reflect the meaning and data type of the value sent to the back end server code.  For example, if I have an HTML select that shows descriptive names to the user, but has DB keys as values, as:

              <select name='productKey' id='selProduct'>
                <option value='123'>Aspirin<option>
                <option value='127'>Tylenol<option>
                <option value='128'>Advil<option>
              </select>

      I might use a name of "productKey" to remind the server code that the request parameter by that name is a numeric database key, and an id of "selProduct" to remind the JavaScript code that it is an HTML select element and therefore has select-type properties like selectedIndex.  If I later change the UI so it is a set of checkboxes. I may change the id to "chkProduct", but I'd leave the name as "productKey".  Such "information hiding" and "separation of concerns" principles are important to large scale software development.

      Also, speaking of checkboxes, sometimes you have to give the same name to several HTML elements, so they send their data as expected, but you may still want separate ids for them, so the JavaScript can manipulate them separately.

      --Fred

    4. Getting dynamic style of an HTML element

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      Here is a Bristle Software JavaScript library function to get the current style of an HTML element.

      /******************************************************************************
      * Get the current style of the specified object.
      *
      * Note:  Can't just use the style property.  That is just the inline style 
      *        specified in the HTML.  This is much more accurate, including 
      *        default values, values set by linked or cascaded stylesheets, and
      *        values set dynamically via DHTML.
      * Note:  Can't just use the currentStyle property.  It is IE-specific.
      ******************************************************************************/
      com.bristle.jslib.Util.getStyle =
      function(obj)
      {
          return ((typeof(obj.currentStyle) != "undefined")
                  ? obj.currentStyle
                  : ((typeof(window.getComputedStyle) != "undefined")
                     ? window.getComputedStyle(obj,"")
                     : obj.style
                    )
                 );
      }

      --Fred

    5. Hiding/showing HTML elements

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      Here are Bristle Software JavaScript library functions to get and set the visibility of an HTML element.

      /******************************************************************************
      * Get the visibility of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.isVisible =
      function(obj)
      {
          return !(com.bristle.jslib.Util.getStyle(obj).display == "none");
      }
      
      /******************************************************************************
      * Set the visibility of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.setVisible =
      function(obj, blnValue)
      {
          // Note:  Set display property to "none" instead of setting visibility 
          //        property to "hidden".  Otherwise the display space consumed 
          //        by the element does not get reused.  The page does not reflow.
          //        The element is invisible but still consumes page space, per
          //        Danny Goodman O'Reilly Dynamic HTML book.
          // Note:  Set display property to "none" instead of setting visibility 
          //        property to "collapse", which is still not recognized by 
          //        Internet Explorer for Windows version 6.0, per Danny Goodman 
          //        O'Reilly Dynamic HTML book.
          obj.style.display = blnValue ? "inline" : "none";
      }

      --Fred

    6. Enabling/disabling HTML elements

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      Here are Bristle Software JavaScript library functions to get and set the disabled property of an HTML element.

      /******************************************************************************
      * Get the disabled property of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.isDisabled =
      function(obj)
      {
          return obj.disabled;
      }
      
      /******************************************************************************
      * Set the disabled property of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.setDisabled =
      function(obj, blnValue)
      {
          obj.disabled = blnValue;
      }

      --Fred

    7. "Fake disabling" HTML elements

      Last Updated: 9/8/2008
      Applies to:  JavaScript 1.0+

      Goal:

      A user interface should be as self-explanatory as possible.  Therefore, all controls should tell you what they do.  Furthermore, all disabled controls should tell you why they are disabled and what you can do to enable them.  

      I use the standard "tooltip" or "bubble help" technique for this.  When you hover the mouse over the control, it pops up a small explanatory text message.  In Web applications, I accomplish this by setting the "title" property of an HTML element, and the hover popups occur automatically.

      This is especially useful in very dynamic user interfaces, where elements are enabled and disabled on the fly for a variety of reasons.  For example, a Delete button may ordinarily be enabled and have a tooltip that says:
              "Delete this ingredient from the formulation"
      However, in a variety of situations, the button may be disabled, and the tooltip may contain one of the following explanations:
              "You are not authorized to delete this ingredient from this formulation"
              "Cannot delete an ingredient that is constrained to have an amount more than zero"
              "Cannot delete before saving.  Click Save to enable this button."
              etc. 

      Problem:

      When I disable an HTML element via its standard "disabled" attribute, the hover popups don't happen.  So, how can you tell why the control is disabled and what you can do to enable it?

      Solution:

      My solution is to leave all elements enabled at the HTML level so that the tooltips work properly, and to fake the behavior of enabled/disabled.  Here are the Bristle Software JavaScript library functions to get and set the "fake disabled" property of an HTML element.

      /******************************************************************************
      * Set the "fake disabled" property of the specified HTML control, so that 
      * it looks and acts disabled, but still supports a title popup that can be
      * used to tell the user why it is disabled.
      *
      * Note: This doesn't actually prevent the events of the control from firing.
      *       To complete the disabled effect, all event procedures of the control 
      *       should call com.bristle.jslib.Util.isFakeDisabled() to decide whether 
      *       to return without doing anything.
      *
      *@param ctl             The control to enable or disable
      *@param blnDisabled     True if disabled; false otherwise.
      *@param strClassName    The CSS class name to assign to the control to change
      *                       its appearance to enabled or disabled.
      *                       Optional.  Can be null or omitted, in which case the
      *                       CSS class is left unchanged.  The empty string is a 
      *                       valid value which can be used to clear the class name.
      *@param strTitle        The value to set as the "title" property (hover popup)
      *                       of the control, typically used to explain its purpose 
      *                       or why it is disabled.
      *                       Optional.  Can be null or omitted, in which case the
      *                       title property is left unchanged.  The empty string 
      *                       is a valid value which can be used to clear the title.
      ******************************************************************************/
      com.bristle.jslib.Util.setFakeDisabled =
      function(ctl, blnDisabled, strClassName, strTitle)
      {
          // Note:  In most browsers, any negative number removes the control from 
          //        the tab sequence, and zero restores its default position in the 
          //        sequence.  However, in some older browsers, the default is -1
          //        instead of zero.  Therefore, use -2 as the special value, not -1.
          ctl.tabIndex  = (blnDisabled ? "-2" : "0");
      
          if ((typeof(strClassName) != "undefined") && (strClassName != null))
          {
              ctl.className = strClassName;
          }
      
          if ((typeof(strTitle) != "undefined") && (strTitle != null))
          {
              ctl.title = strTitle;
          }
      }
      /******************************************************************************
      * Get the "fake disabled" property of the specified HTML control.
      ******************************************************************************/
      com.bristle.jslib.Util.isFakeDisabled =
      function(ctl)
      {
          return ("-2" == ctl.tabIndex);
      }

      --Fred

    8. Bolding/unbolding HTML text

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      Here are Bristle Software JavaScript library functions to get and set the bold property of an HTML element.

      /******************************************************************************
      * Get the boldness of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.isBold =
      function(obj)
      {
          return (com.bristle.jslib.Util.getStyle(obj).fontWeight == "bold");
      }
      
      /******************************************************************************
      * Set the boldness of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.setBold =
      function(obj, blnValue)
      {
          obj.style.fontWeight = blnValue ? "bold" : "normal";
      }

      --Fred

    9. Setting color

      Last Updated: 4/17/2007
      Applies to:  JavaScript 1.0+

      Here are Bristle Software JavaScript library functions to get and set the foreground and background colors of an HTML element.

      /******************************************************************************
      * Get the color of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.getColor =
      function(obj)
      {
          return (com.bristle.jslib.Util.getStyle(obj).color);
      }
      
      /******************************************************************************
      * Set the color of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.setColor =
      function(obj, strValue)
      {
          obj.style.color = strValue;
      }
      
      /******************************************************************************
      * Get the background color of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.getBackgroundColor =
      function(obj)
      {
          return (com.bristle.jslib.Util.getStyle(obj).backgroundColor);
      }
      
      /******************************************************************************
      * Set the background color of the specified object.
      ******************************************************************************/
      com.bristle.jslib.Util.setBackgroundColor =
      function(obj, strValue)
      {
          obj.style.backgroundColor = strValue;
      }

      --Fred

  3. DOM Events

    1. Handling Events

      1. Handling Events via HTML Attributes

        Last Updated: 11/1/2007
        Applies to:  JavaScript 1.0+

        Various "events" occur in the Web browser, in response to user interactions, timers, pages loading, etc, and you can assign snippets of JavaScript code to "handle" the events.

        Some particularly useful events include:

        Type Events
        Web page load, unload, resize
        Mouse buttons click, dblclick, mousedown, mouseup
        Mouse pointer mousemove, mouseover, mouseout
        Keyboard keypress, keydown, keyup, blur, focus
        Data entry fields change, select

        The typical way to assign an event handler is via the HTML "on" event handler attributes of the HTML elements.  For example, to cause the JavaScript routine objBody_onLoad() to be called when the Web page finishes loading:

                <body id='objBody' onload='return objBody_onLoad(event, this)'>

        Similarly:

                <input id='txtName'
                       type='text' 
                       onchange='return txtName_onChange(event, this)'
                       onkeyup='return txtName_onKeyUp(event, this)'
                       onmouseup='return txtName_onMouseUp(event, this)'
                       onmousemove='return txtName_onMouseMove(event, this)'
                       onmouseover='return txtName_onMouseOver(event, this)'
                       onmouseout='return txtName_onMouseOut(event, this)'
                       />

        causes the various specified routines to be called whenever the text field value changes, when a keyboard or mouse key is released in the text box, when the mouse moves within the text box, and when the mouse moves into or out of the text box.

        Notes:

        1. The value of each "on" attribute can be a quoted string containing any JavaScript snippet.  However, I always use a simple call to a single JavaScript function that contains the JavaScript code that I could otherwise have put directly into the quoted string.  This makes my HTML simpler and easier to read, keeping the JavaScript code in a separate function, which can even reside in a separate JavaScript file.
        2. If you use such a JavaScript function as an event handler, it can have any name.  For the sake of readability and maintainability, I always name mine with the exact id of the object, followed by "_on", followed by the exact name of the event in mixed upper and lower case.  (This is the same convention used by VBScript to automatically find the event handler if you don't specify the "on" attribute.)  This convention documents the situation in which each JavaScript function is called, so I don't need to mention that in my comments.  The comment header I put on each such JavaScript function explains what it does when called, but not what causes it to be called.
        3. You can pass any parameters to the JavaScript function.  I always pass the two special variables event and this.  These are the event that occurred to cause the handler to be called and the object to which the event occurred.  Both of these have useful properties and methods that can be accessed from within the JavaScript function.  For example, when the event is a mousemove event, it has properties to specify the exact location of the mouse pointer, and when the event is a keyup event, it has properties to specify which key was released.  Also, the object has properties and methods to get/set its color, visibility, etc. 
        4. I always put the keyword return in front of the call to the JavaScript function.  This returns the value of the JavaScript function to the browser, which makes it possible for the JavaScript function to cancel the event by returning false.  For example, you can cancel a keyup event whenever the key was anything except a numeric digit to prevent the user from entering non-numeric values into a text box. 

        Here's an example of such an event handler.  It turns the text field red when the mouse moves into it.

                function txtName_onMouseOver(evt, obj)
                {
                    obj.style.backgroundColor = "red";
                    return true;
                }

        --Fred

      2. Handling Events via DOM Properties

        Last Updated: 11/1/2007
        Applies to:  JavaScript 1.0+

        Another way to assign an event handler is via the "on" properties of the DOM objects.  For example:

                txtName.onchange = txtName_onChange;

        This DOM property approach is more dynamic than the HTML attribute approach because you can assign a new event handler on the fly.  Also, this approach allows you to assign event handlers to objects that don't occur in the HTML, like the document itself.

        However, it requires that you specify the name of a JavaScript function, without specifying what parameters to pass.  In most browsers, such an event handler is always called with one parameter -- the event.  In Internet Explorer, such an event handler is called with no parameters, so you have to use the global window.event object to determine the current event.  Once you know the event, you can determine the object to which the event occurred via the event.target property in most browsers.  In Internet Explorer, you must use the event.srcElement property.  

        If the event handler is assigned in this way, it has more work to do:

                function txtName_onMouseOver(evt)
                {
                    // Get event, which IE does not pass.
                    if (typeof(evt) == "undefined")
                    {
                         evt = window.event;
                    }
        
                    // Get the object to which the event occurred.
                    var obj;
                    if (typeof(evt.target) != "undefined")
                    {
                        // Most browsers
                        obj = evt.target;
                    }
                    else if (typeof(evt.srcElement) != "undefined")
                    {
                        // Internet Explorer
                        obj = evt.srcElement;
                    }
                    else 
                    {
                        // Can't tell which object.  What to do?
                        return true;
                    }
        
                    // Finally, set the color to red.
                    obj.style.backgroundColor = "red";
                    return true;
                }

        --Fred

    2. Getting the Event keyCode

      Last Updated: 11/1/2007
      Applies to:  JavaScript 1.0+

      Here is the Bristle Software JavaScript library function to determine which key was pressed in a keyboard event:

      /******************************************************************************
      * Get the keyCode from the specified event.
      *
      *@param evt     The event containing the keyCode
      *@return        The keyCode, or null.
      *@throws        None.
      ******************************************************************************/
      com.bristle.jslib.Event.getEventKeyCode =
      function(evt)
      {
          if (!evt) return null;
          if (evt.keyCode && evt.keyCode != 0)
          {
              return evt.keyCode;
          }
          else if (evt.charCode)      // Netscape uses charCode, not keyCode, for
                                      // some events.
          {
              return evt.charCode;
          }
          else
          {
              return null;
          }
      }

      --Fred

  4. Controlling the Browser

    1. Disabling the Backspace key

      Last Updated: 11/1/2007
      Applies to:  JavaScript 1.0+

      Want a way to prevent the Backspace key from clicking the browser Back button in your Web application?

      Problem:

      Modern Web applications often have large complex data entry screens.  Your user may spend a significant amount of time filling them out before finally submitting them to the Web server.  Much of the data is entered via text fields, which means he is likely to hit the Backspace key often. However, the default browser behavior when he hits Backspace outside of a text box is to click the browser's Back button.  Once the back button has been clicked, there is no way for you, the JavaScript programmer, to prevent the browser from discarding the current page and returning to the previous page. The best you can do is take some action on the way out, like prompting the user and attempting to save the data if it has no validation errors.  This is a common and frustrating user error.

      Solution:

      The solution is to disable the Backspace key as a shortcut for the Back button, on all of your complex data entry screens, taking care to not disable its main function of deleting typed characters.  With the Backspace key disabled, your user can still return to the previous page by hitting Alt-Left Arrow, or by clicking on the Back button with the mouse, which he is less likely to do by accident.

      Here is the Bristle Software JavaScript library code to prevent Backspace from causing a browser Back operation.  To use it, simply put the following call in your objBody_onLoad() event handler:

          com.bristle.jslib.Event.disableBackspaceAsBrowserBackButton();

      and paste the following (along with the getEventKeyCode() routine from the previous tip) into the HTML page or an included JavaScript source file:

      com.bristle.jslib.Event.intKEYCODE_BACKSPACE   = 8;
      
      /******************************************************************************
      * Return true if the specified event is the Backspace key.
      *
      *@param evt     The event containing the keyCode
      *@return        True if the event was the Backspace key.
      *@throws        None.
      ******************************************************************************/
      com.bristle.jslib.Event.isBackspaceKey =
      function(evt)
      {
          if (com.bristle.jslib.Event.getEventKeyCode(evt) == 
                                          com.bristle.jslib.Event.intKEYCODE_BACKSPACE)
          {
              return true;
          }
          return false;
      }
      
      /******************************************************************************
      * Return false if the specified event is the Backspace key as a shortcut for 
      * the browser Back button, true if it is being used to delete chars in an 
      * enabled text box or if it is not a Backspace at all.
      *
      *@param evt Event to check for backspace key.
      ******************************************************************************/
      com.bristle.jslib.Event.isNotBackspaceAsBrowserBackButton = 
      function(evt) 
      {
          // Get event which IE does not pass as a param like other browsers do.
          if (typeof(evt) == "undefined")
          {
               evt = window.event;
          }
      
          if (!com.bristle.jslib.Event.isBackspaceKey(evt))
          {
              return true;
          }
      
          // Note: None of these are needed.  Returning false is sufficient.
          // if (typeof (evt.returnValue) != "undefined")
          // {
          //     evt.returnValue = false;
          // }
          // if (typeof (evt.cancelBubble) != "undefined")
          // {
          //     evt.cancelBubble = true;
          // }
          // if (typeof (evt.stopPropagation) != "undefined")
          // {
          //     evt.stopPropagation();
          // }
          // if (typeof (evt.keyCode) != "undefined")
          // {
          //     evt.keyCode = 0;
          // }
      
          // Get the HTML element in which Backspace was typed.
          var targetNode;
          if (typeof(evt.target) != "undefined")
          {
              // Most browsers
              targetNode = evt.target;
          }
          else if (typeof(evt.srcElement) != "undefined")
          {
              // Internet Explorer
              targetNode = evt.srcElement;
          }
          else 
          {
              // Can't tell where the Backspace was pressed.  Don't cancel it.
              return true;
          }
      
          // Don't cancel Backspace for these HTML elements except when they
          // are readonly.  When not readonly, they make good use of Backspace 
          // (to delete chars), and they prevent it from causing the browser 
          // Back operation anyhow.
          // Cancel Backspace for all other HTML elements.
          var strNodeName = targetNode.nodeName;
          if (   (strNodeName == "INPUT" || strNodeName == "TEXTAREA")
              && !targetNode.readOnly)
          {
              return true;
          }
          else
          {
              return false;
          }
      }
      
      /******************************************************************************
      * Disable the Backspace key as a shortcut for the browser Back button, 
      * but allow it to delete chars in enabled text boxes.
      ******************************************************************************/
      com.bristle.jslib.Event.disableBackspaceAsBrowserBackButton =
      function()
      {
          // Note: This must be assigned via the onkeydown property of the document
          //       object, not as an HTML attribute of the HTML body element.
          //       Otherwise, it doesn't work in IE 6.0.2800.1106.
          document.onkeydown=com.bristle.jslib.Event.isNotBackspaceAsBrowserBackButton;
      }

      --Fred

    2. Showing a temporary statusbar message

      Last Updated: 11/8/2007
      Applies to:  JavaScript 1.0+

      Here is the Bristle Software JavaScript library function to display a text message in the statusbar of the browser, temporarily for the specified number of seconds:

      /******************************************************************************
      * Show a text message in the status bar of the browser's window, optionally
      * clearing it after a specified number of seconds.
      * Note: In Internet Explorer IE 6.0.2800.1106, the screen is updated to show
      *       the status immediately.  In Firefox 2.0.0.9, the screen is updated
      *       only after the currently running JavaScript code completes and
      *       control is returned to the browser.  Therefore, in Firefox, this is
      *       not useful for showing the status of multiple steps in a single
      *       long-running block of JavaScript code inside a single event handler.
      * Anticipated Changes:
      * - Does not yet check for apostrophe chars in the message, or any other
      *   chars that could cause syntax errors in calling setTimeout.  For now,
      *   the caller should double all apostrophes.
      *
      *@param strMessage  Message to show
      *@param intSeconds  Number of seconds before clearing the message.
      *                   Optional.  Default: Don't clear it.
      ******************************************************************************/
      com.bristle.jslib.Util.strStatusBarMessageToBeCleared = "";
      com.bristle.jslib.Util.showStatusBarMessage =
      function(strMessage, intSeconds)
      {
          window.status = strMessage;
          if (typeof(intSeconds) != "undefined")
          {
              // Schedule a code snippet after the specified number of seconds
              // that will clear the status bar only if the original message is
              // still being shown at that time.
              // Note: Prefix intSeconds with "0" and use parseInt() to guarantee
              //       a valid numeric value, defaulting to zero.
              com.bristle.jslib.Util.strStatusBarMessageToBeCleared = strMessage;
              intSeconds = parseInt("0" + intSeconds, 10);
              window.setTimeout
                  (  "if (com.bristle.jslib.Util.strStatusBarMessageToBeCleared"
                   + "    == '" + strMessage + "') "
                   + "{ com.bristle.jslib.Util.showStatusBarMessage(''); }"
                  ,intSeconds * 1000
                  ,"JavaScript");
          }
      }

      --Fred

  5. Ajax

    1. Ajax Intro

      1. What is Ajax?

        Original Version: 3/31/2007
        Last Updated: 12/10/2007
        Applies to:  JavaScript 1.0+, IE 5.5+, Firefox 1+, Netscape 7+, Safari 1+

        Ajax is a technique, not a programming language or any other new technology.  It requires no additional software.

        The Ajax technique can be used in any Web application, regardless of whether the server-side code is written in Java, .NET, Ruby on Rails, PHP, Perl, C/C++, or whatever.

        The term "Ajax" was coined in 2005 to describe a technique that some JavaScript programmers had been using since 1997 or so, and that I personally started using in 2000.  It stands for "Asynchronous JavaScript and XML".  The dramatic use of this technique by Google Maps in 2005, and the subsequent coining of a catchy name, has catapulted Ajax into the mainstream of Web programming.

        Ajax allows a Web page to go back to the server for more data without the user being aware of it, and without having to rebuild the whole web page and lose the user's context.  For example, Google Maps uses it to allow you to drag a map and have new map regions scroll into view.  It is typically implemented via JavaScript code using the XMLHttpRequest object (which is built into all modern browsers) to get data from the server, and then using DHTML (Dynamic HTML, which is also built into all modern browsers) to update the current Web page.

        However, the name is somewhat of a misnomer because Ajax:

        1. Can be synchronous or asynchronous, though asynchronous is most common.
        2. Does not necessarily involve JavaScript.  The client-side code is typically written in JavaScript, but could also be PerlScript, ActiveScript, VBScript, or many other client-side browser scripting languages.
        3. Does not necessarily use XML.  It is commonly used to transmit XML, JSON, plain text, or any other data format.

        Furthermore, Ajax is not limited to running in Web browsers.  The same technique can be used to access Web services from within any application that is capable of creating and using either the standard XMLHttpRequest object or the Microsoft MSXML ActiveX object to access data from a Web service.  Therefore, it can be used from:

        1. VBA in MS Word, Excel and PowerPoint
        2. LotusScript in Lotus Notes
        3. AutoLisp in AutoCAD
        4. etc.  

        It can also be used in your own custom client applications, written in Java, C, C++, C#, VB, etc.  It can even be used in your server-side applications to access Web services on other servers. 

        --Fred

      2. Why use Ajax?

        Original Version: 3/31/2007
        Last Updated: 12/10/2007

        There are several business-oriented (non-geeky) reasons to use Ajax:
        1. Users demand it
          Ajax is not just another cool but geeky technique that programmers foist off on their users.  It is something that the users demand to have, once they've seen it.  It allows for very user-visible effects that provide a very rich user interface within a Web page.  Once they've used Ajax-driven Web pages, they'll never be satisfied with the old way again.
        2. Portable across browsers
          With slight variations that are easily worked around, Ajax is built into all modern Web browsers.
        3. Zero-install, no "DLL Hell"
          Because it is already built into the web browser, Ajax is already installed on every client computer.  Your Web application, including its Ajax code can already be used from any computer, with no need to install Ajax itself or any of your Ajax code.  Therefore, there is no "DLL Hell", where an install of one app can break another previously installed app.
        4. Flexibility and Speed
          Ajax allows a clean separation of data from the user interface, providing new options in choosing the right mix of static and dynamic pieces to achieve the right mix of flexibility and efficiency.

          In traditional client/server apps, the forms, controls, and code are typically static, and the data is dynamic.  For example, a static form, with a static table, and a static set of buttons, typically shows a dynamic set of data.  Each user interaction continues to use the same form, table, and buttons, but may load new data.  This is very efficient, but not very flexible.

          In traditional web apps, on the other hand, the forms, controls, code, and data are all dynamic.  For example, the Web server runs code that dynamically generates a form, with a table and a set of buttons, and dynamically generates the data to show in the table.  Each user interaction regenerates the form, table, and buttons, in addition to loading new data.  This is very flexible, but not very efficient.  As a result it is noticeably slow.  Users have come to tolerate the slowness because of the other advantages that Web apps offer (zero install, portability, etc.)

          Ajax offers the best of both worlds.  The data can be dynamic, while the forms, controls, and code are any mix of static and dynamic.  Like a traditional client/server app, the Ajax app can load the forms, controls, and code once, and just change the data dynamically.  The entire Ajax app can run inside a single Web page, loaded once from the server, with Ajax going back to the server occasionally for new data only.  Or, like a traditional web app, the Ajax app can go back to the server more often for new pages, containing new forms, controls, code, and data.  Or, Ajax can dynamically modify the forms, controls, code and data on the fly, without loading a new Web page from the server.

        This last reason may seem like a geeky reason, because the users don't typically care about such separation.  However it really is a business-oriented reason because it allows the Ajax app to create a much better user interface.  This is the reason that really causes users to demand Ajax.

        There are also some truly geeky reasons to use Ajax.  The users don't care about these reasons and may never understand how they contribute to the user experience, but the programmers do.

        1. Multi-threaded data retrieval from Web servers
          Traditional Web apps are single-threaded, except for the concurrent loading of images.  JavaScript has always allowed multiple threads to do cosmetic things like animations of banners and such.  However, Ajax finally allows multi-threaded access to the Web server and through it to the database.  This allows you to do things like:
          1. Pre-fetch data before needed
            You can pre-fetch data before it is needed to create the appearance of speed.  This is the secret of much of the success of Google Maps.  It doesn't wait for you to drag left, right, up, or down before fetching the additional map data in that direction.  It pre-fetches some in each direction, and then continues pre-fetching in the direction you are currently going.
          2. Progress indicators
            Ajax operations provide "callbacks" for your JavaScript code to be notified that progress is being made, not only that the entire operation succeeded or failed.  You can use these callbacks to manage an animated progress indicator to give the user feedback about the ongoing operation.
          3. Partial data load
            You can load a subset of the data requested by the user, and allow the user to begin viewing and editing that data, while you continue to load more data behind the scenes.
          4. No need for window.setTimeout()
            Since Ajax has built is support for both synchronous and asynchronous operation, you don't need to use window.setTimeout() to explicitly create a new thread for it.
        2. "Mashups" -- Merged data from multiple Web servers
          Ajax allows you to retrieve data from multiple Web servers and merge it all into one Web page, a technique that has recently been named a "mashup".  This has led to a proliferation of Web sites that show real estate data, classified ads, etc. all superimposed over a Google map.  You can write your own mashup to merge data from a variety of sources in any way you like.  Many Web sites now explicitly offer their data as Web Services and other XML streams to make it more convenient for you to use their data.
          Note: For Web browser security reasons, the Ajax code can't go directly to the various servers, so you'll have to funnel it all through your own Web server, the one that served the main page to the browser.  Alternatively, you can bypass the browser security by rolling your own Ajax via the SRC property of the HTML IFRAME and SCRIPT tags instead of using the XMLHttpRequest object.  Finally, if you only have a limited set of users, you can have them change their browser security settings to be less restrictive.
        3. Less bandwidth required; less server load
          Ajax allows you to go back to the Web server much more often, but for much smaller chunks of data.  You can fetch a partial page, or even just new data to insert into a page, without having to re-fetch the entire page.  The result can be much less total traffic to the Web server, reducing both the load on the server and the bandwidth requirements.

        --Fred

      3. How much to use Ajax?

        Original Version: 3/31/2007
        Last Updated: 12/10/2007

        Ajax is not an all-or-nothing proposition.  You don't have to abandon all of your old Web software and convert everything to Ajax.  It is just one more technique in your programming bag of tricks.  Take an existing app, and add a few jazzy effects via Ajax.  If that works out well, begin adding more.  No major re-write required.

        In subsequent tips, I'll be showing details of how to use Ajax to:

        1. Send a periodic "keep-alive" to the Web server, to keep the server session from timing out while your user temporarily walks away from a complex form that is halfway filled out.
        2. Drill down into data, showing more detail.
        3. Do validation of data entry fields at the server on the fly, even at each user keystroke.
        4. Continue adding to the bottom of an HTML table while the user interacts with the top of it.
        5. etc.

        --Fred

      4. See Also

        Original Version: 3/31/2007
        Last Updated: 12/10/2007

        For more info on Ajax, see the Ajax row of my links page:

            http://bristle.com/~fred/#ajax

        For a quick intro to Ajax, see my PowerPoint presentation at:

            http://bristle.com/~fred/ajaxdemo/AJAXIntro.ppt

        For a variety of Ajax demos see:

            http://bristle.com/~fred/#ajax_demos

        1. A very simple demo I wrote myself
        2. A collection of small demos in a single page by Steve Benfield
        3. Google Suggest (like regular Google, but pre-fetches results)
        4. Google Maps (pre-fetches map data)
        5. Google Finance (dynamically loads stock data)
        6. Language translation (translate words as you type them)
        7. Mouse gesture as password (track the mouse motion via Ajax)
        8. Typing speed as password (measure time between keystrokes)
        9. Classified ads tied to a map

            http://bristle.com/~fred/#mashups

        1. HousingMaps (Google Maps + CraigsList)
        2. MashMap (Google Maps + ShowTimes)
        3. Zillow (Google Maps + Real Estate)

        Remember, since it is all JavaScript embedded in the Web page, you can see how they do it via View Source, as explained in my tips: 

                View Source
                View Source of Referenced JavaScript and CSS Files
                Firefox View Live Source

        --Fred

    2. Simple Ajax Example

      Original Version: 12/4/2007
      Last Updated: 12/13/2007
      Applies to:  JavaScript 1.0+

      Here is a simple complete example of JavaScript code that uses Ajax:

              var xhr = new XMLHttpRequest();
              xhr.onreadystatechange = myHandler;
              xhr.open("GET", "my_servlet", true);
              xhr.send("p1=abc");
      
              ...
      
              function myHandler() 
              {
                  if (xhr.readyState == 4) 
                  {
                      doSomethingWith(xhr.responseXML);
                  }
                  else if (xhr.readyState == 3)
                  {
                      showProgressIndicator();
                  }
              }

      In subsequent tips, I'll explain what is going on here, and show a slightly different version of the first line that is required by Microsoft Internet Explorer.  But, generally speaking, it really is that simple.

      --Fred

    3. XMLHttpRequest Methods

      Last Updated: 12/5/2007
      Applies to:  JavaScript 1.0+

      These are all of the methods of the XMLHttpRequest object:

          open ("method", "URL", [async, username, password])
                  Assigns destination URL, method, etc.

          send (params)
                  Sends request including postable string or DOM object data

          abort ()
                  Terminates current request

          getAllResponseHeaders ()
                  Returns headers (name/value pairs) as a string

          getResponseHeader ("header")
                  Returns value of a given header

          setRequestHeader ("label","value")
                  Sets Request Headers before sending

      --Fred

    4. XMLHttpRequest Properties

      Original Version: 12/5/2007
      Last Updated: 1/11/2008
      Applies to:  JavaScript 1.0+

      These are all of the properties of the XMLHttpRequest object:

          onreadystatechange
                  Event handler (your code) that fires at each state change

          readyState
                  0 = uninitialized
                  1 = loading
                  2 = loaded
                  3 = interactive 
         
                       (Some data has been returned.  In IE, the event 
                           handler fires only once.  In all other browsers,
                           it fires periodically, so you can show a progress bar.)
                  4 = complete

          status
                  HTTP status code returned from server.
                  200-299 = OK

          statusText
                  Message text to go with HTTP status code

          responseText
                  String version of data returned from server

          responseXML
                  XML DOM document of data returned

      --Fred

    5. Bristle Ajax Library

      1. Create an Ajax Object

        Original Version: 12/12/2007
        Last Updated: 1/10/2008
        Applies to:  JavaScript 1.0+, IE 5.5+, Firefox 1+, Netscape 7+

        Generally speaking, the Ajax object is the same in all browsers.  

        However, there is one notable exception.  Microsoft Internet Explorer does not yet support the standard way of creating the object, and requires browser-specific code.  Once created, the Ajax object supports the same properties and methods in all browsers, so only the creation code has to be browser-specific.

        Therefore, the Simple Ajax Example is not portable.  It works fine in all browsers except Internet Explorer, where the first line must be changed to the Microsoft way of creating the Ajax object.

        To simplify things, it makes sense to have a single routine that supports creation in any browser, so you can change the first line of Simple Ajax Example from:

        	var xhr = new XMLHttpRequest();

        to something portable but equally simple like:

        	var xhr = com.bristle.jslib.Ajax.Util.createAjaxObject();

        Here is a Bristle Software JavaScript Library routine that does just that:

        /******************************************************************************
        * Returns a new XMLHttpRequest object for use with Ajax operations.
        *
        *@throws com.bristle.jslib.Exception.intEXC_UNABLE_TO_CREATE_AJAX_OBJECT
        ******************************************************************************/
        com.bristle.jslib.Ajax.Util.createAjaxObject =
        function()
        {
            var xhr = null;
            if (window.XMLHttpRequest) 
            {
                // Create XMLHttpRequest object in most browsers.
                xhr = new XMLHttpRequest();
            }
            else if (window.ActiveXObject)
            {
                // Create XMLHttpRequest object in Microsoft browsers.
                try 
                {
                    // Newer versions of Internet Explorer
                    xhr = new ActiveXObject("Msxml2.XMLHTTP");
                }
                catch (e1)
                {
                    try 
                    {
                        // Older versions of Internet Explorer
                        xhr = new ActiveXObject("Microsoft.XMLHTTP");
                    }
                    catch (e2)
                    {
                        throw new com.bristle.jslib.Exception.Exception
                        (com.bristle.jslib.Exception.intEXC_UNABLE_TO_CREATE_AJAX_OBJECT
                        ,"Unable to create Ajax object"
                        ,"com.bristle.jslib.Ajax.Util.createAjaxObject"
                        );
                    }
                }
            }
            else
            {
                throw new com.bristle.jslib.Exception.Exception
                (com.bristle.jslib.Exception.intEXC_UNABLE_TO_CREATE_AJAX_OBJECT
                ,"Unable to create Ajax object"
                ,"com.bristle.jslib.Ajax.Util.createAjaxObject"
                );
            }
            return xhr;
        }

        --Fred

      2. Register Ajax Callbacks

        Original Version: 12/12/2007
        Last Updated: 12/13/2007
        Applies to:  JavaScript 1.0+, IE 5.5+, Firefox 1+, Netscape 7+

        As shown in Simple Ajax Example, the next step after creating the Ajax object is typically to register the "onreadystatechange" callback function that will be called at various times as the Ajax object changes state from "uninitialized" to "loading" to "loaded" to "interactive" and finally to "complete".  This single callback is called multiple times, once at each state change.  In all browsers except Microsoft Internet Explorer, it is also called periodically as the request is being processed but remains in the "interactive" state, so that you can use it to maintain a visual progress bar or something.

        Unfortunately, there is no built-in support for a timeout (if the server is too slow or never responds to the Ajax request).  Also, you typically have to write standard boilerplate code inside the single callback to check the readyState property to determine each time why the callback was called, and to check the HTTP status code to see whether a completed Ajax request succeeded or failed. 

        It makes sense to have a single routine that supports timeouts, and supports different callbacks for different situations, with simple calls like:

        	com.bristle.jslib.Ajax.Util.registerAjaxCallbacksAndStartTimer
        		(xhr
                	,callbackSuccess
                	,callbackFailure
                	,10000 // Timeout in 10 seconds
                	,callbackTimeout
                	,callbackInteractive
                	,callbackLoaded
                	,callbackLoading
                	,callbackUninitialized);

        Then Simple Ajax Example can be re-written as:

                var xhr = com.bristle.jslib.Ajax.Util.createAjaxObject();
        	com.bristle.jslib.Ajax.Util.registerAjaxCallbacksAndStartTimer
        		(xhr
                	,doSomethingWith
                	,null
                	,null
                	,null
                	,showProgressIndicator);
                xhr.open("GET", "my_servlet", true);
                xhr.send("p1=abc");

        Here is a Bristle Software JavaScript Library routine to do that:

        /******************************************************************************
        * Registers the specified functions as callbacks for the specified 
        * XMLHttpRequest object, and starts the timeout timer.  The callback functions 
        * will be called asynchronously in response to the various state changes of 
        * the XMLHttpRequest as it processes Ajax requests.
        *
        * Notes:
        * - Each callback is optional.  Trailing callback params can be omitted 
        *   entirely.  When not omitted, each callback can be specified as null, 
        *   or as a JavaScript function with the signature:
        *       function(XMLDOM, XMLHttpRequest)
        *   where the 1st parameter is the XML DOM Document returned by the 
        *   responseXML property of the XMLHttpRequest, and the 2nd parameter is
        *   the XMLHttpRequest object itself.  The 2nd parameter is optional, 
        *   which allows callbacks to ignore the XMLHttpRequest object and simply
        *   operate on the XML DOM Document passed in the 1st parameter, as:
        *       xmlDOM.getElementsByTagName("name")[0].firstChild.nodeValue
        *   More advanced callbacks can use the 2nd parameter to access the other 
        *   methods of the XMLHttpRequest object:
        *       abort()
        *       getResponseHeader()
        *       getAllResponseHeaders()
        *       etc.
        *   and the other properties:
        *       readyState
        *       status
        *       statusText
        *       responseText
        *       etc.
        *   These can be especially useful if the same callback is registered to 
        *   handle multiple states, or both success and failure statuses of the 
        *   Complete state.  Or to handle non-XML responses like HTML, JSON, or plain
        *   text, by using responseText instead of responseXML.  Also, callbacks
        *   other than callbackSuccess will receive a null XML DOM and may have to 
        *   use the XMLHttpRequest parameter to determine the details of the
        *   HTTP progress or failure. 
        *
        *@param xhr                 The XMLHttpRequest object
        *@param callbackSuccess     Function to be called when the state is Complete, 
                                    and the HTTP status indicates success.
        *@param callbackFailure     Function to be called when the state is Complete, 
                                    and the HTTP status indicates failure.
        *@param intTimeoutMillisecs Number of seconds before aborting the Ajax call
        *                           and calling callbackTimeout.
        *                           Optional.  Default=0 which means wait forever.
        *@param callbackTimeout     Function to be called if the Ajax call times out.
        *@param callbackInteractive Function to be called when the state is Interactive
        *@param callbackLoaded      Function to be called when the state is Loaded
        *@param callbackLoading     Function to be called when the state is Loading
        *@param callbackUninitialized
        *                           Function to be called when the state is Uninitialized
        *@return  Handle to setTimeout() used for callbackTimeout so it can be 
        *         canceled by the caller via clearTimeout() if necessary.
        ******************************************************************************/
        com.bristle.jslib.Ajax.Util.AJAX_STATE_COMPLETE      = 4;
        com.bristle.jslib.Ajax.Util.AJAX_STATE_INTERACTIVE   = 3;
        com.bristle.jslib.Ajax.Util.AJAX_STATE_LOADED        = 2;
        com.bristle.jslib.Ajax.Util.AJAX_STATE_LOADING       = 1;
        com.bristle.jslib.Ajax.Util.AJAX_STATE_UNINITIALIZED = 0;
        com.bristle.jslib.Ajax.Util.HTTP_STATUS_SUCCESS = 200;
        com.bristle.jslib.Ajax.Util.registerAjaxCallbacksAndStartTimer =
        function(xhr
                ,callbackSuccess
                ,callbackFailure
                ,intTimeoutMillisecs
                ,callbackTimeout
                ,callbackInteractive
                ,callbackLoaded
                ,callbackLoading
                ,callbackUninitialized)
        {
            // Schedule a call to callbackTimeout.
            // Note: Use a closure (anonymous function) to keep track of which 
            //       XMLHttpRequest to pass to callbackTimeout when there are multiple
            //       XMLHttpRequests instead of one global singleton.
            // Note: Shouldn't really start the timer until we make the Ajax call.  
            //       Not now, when we are just registering the callbacks.  Should move 
            //       this code to getAjaxXML().  But, the callbacks need to know what 
            //       timer to cancel when the Ajax completes with failure or success, 
            //       so we need a handle to the timer when creating the callbacks, 
            //       and we don't know how get a handle to a timer without starting 
            //       the timer.  Could wrap the timer in another object that creates it
            //       when told and keeps a handle to it, and could then pass a handle
            //       to that timer to the callbacks.  Maybe later.  For now, this is
            //       sufficient, since most use is via getAjaxXML() which makes the
            //       Ajax call immediately after registering the callbacks.
            var timer = null;
            if (intTimeoutMillisecs != null 
                && typeof(intTimeoutMillisecs) != "undefined"
                && intTimeoutMillisecs > 0 )
            {
                timer = window.setTimeout
                   (function() 
                    {
                        if (callbackTimeout != null 
                            && typeof(callbackTimeout) != "undefined")
                        {
                            callbackTimeout(null, xhr);
                        }
                        xhr.abort();
                    }
                   ,intTimeoutMillisecs
                   ,"JavaScript"
                   );
            }
        
            // Note: Use a closure (anonymous function) to keep track of which 
            //       XMLHttpRequest to pass to the callbacks when there are multiple
            //       XMLHttpRequests instead of one global singleton.
            var callback = null;
            xhr.onreadystatechange = 
            function()    
            {
                // Determine which callback to call.
                if (xhr.readyState == com.bristle.jslib.Ajax.Util.AJAX_STATE_COMPLETE)
                {
                    // Cancel the timeout immediately.  The operation has completed.
                    // Note: The completion may have been caused by the timer's call
                    //       to xhr.abort(), so the timer may already have fired.
                    //       Canceling it after it fires is not a problem.
                    if (timer != null)
                    {
                        window.clearTimeout(timer);
                    }
        
                    // Catch any errors that occur while trying to determine the 
                    // reason for the completion of the operation.  Reasons can be:
                    // 1. Normal completion with successful HTTP status code.
                    // 2. Normal completion with error HTTP status code.
                    // 3. Call to xhr.abort()
                    //    Note: We could probably prevent this case, if we ever needed 
                    //          to, by changing the value of xhr.onreadystatechange to 
                    //          no longer refer to this code before calling xhr.abort().
                    // 4. HTTP server was already down or inaccessible when Ajax call
                    //    was made.
                    // 5. HTTP server went down or became inaccessible before Ajax call
                    //    completed normally.
                    // We test xhr.status to distinguish between cases 1 and 2.
                    // However, with Firefox 2.0.0.10, testing it in cases 3, 4, and 5 
                    // causes an exception:
                    //        Component returned failure code: 0x80040111 
                    //        (NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.status]
                    // to be thrown.
                    try
                    {
                        if (xhr.status == com.bristle.jslib.Ajax.Util.HTTP_STATUS_SUCCESS)
                        {
                            callback = callbackSuccess;
                        }
                        else
                        {
                            callback = callbackFailure;
                        }
                    }
                    catch(exception)
                    {
                        // Treat exception while checking status as a failure.
                        // This is to act the same in browsers where such an 
                        // exception occurs as in browsers where it does not.
                        // Since something has gone wrong that causes an exception 
                        // in some browsers, other browsers would most likely not
                        // have found an HTTP success code, and would have treated
                        // this as a failure.
                        callback = callbackFailure;
                    }
                }
                else if (xhr.readyState == com.bristle.jslib.Ajax.Util.AJAX_STATE_INTERACTIVE)
                {
                    callback = callbackInteractive;
                }
                else if (xhr.readyState == com.bristle.jslib.Ajax.Util.AJAX_STATE_LOADED)
                {
                    callback = callbackLoaded;
                }
                else if (xhr.readyState == com.bristle.jslib.Ajax.Util.AJAX_STATE_LOADING)
                {
                    callback = callbackLoading;
                }
                else if (xhr.readyState == com.bristle.jslib.Ajax.Util.AJAX_STATE_UNINITIALIZED)
                {
                    callback = callbackUninitialized;
                }
        
                // Call the callback.
                if (callback != null && typeof(callback) != "undefined")
                {
                    callback(xhr.responseXML, xhr);
                }
            }
        
            return timer;
        }

        --Fred

      3. Get XML via Ajax

        Original Version: 12/12/2007
        Last Updated: 12/13/2007
        Applies to:  JavaScript 1.0+, IE 5.5+, Firefox 1+, Netscape 7+

        The next step after creating the Ajax object and registering the callback function(s) is typically to call open() and send() and then wait for the callbacks to occur.  

        To simplify things even further, it makes sense to have a single routine that does it all, so Simple Ajax Example becomes a one-liner:

                com.bristle.jslib.Ajax.Util.getAjaxXML
        		("my_servlet?p1=abc"
                	,doSomethingWith);

        or to preserve the progress indicator:

                com.bristle.jslib.Ajax.Util.getAjaxXML
        		("my_servlet?p1=abc"
                	,doSomethingWith
                	,null
                	,null
                	,null
                	,showProgressIndicator);

        or if you have more callbacks and a timeout:

        	com.bristle.jslib.Ajax.Util.getAjaxXML
        		("my_servlet?p1=abc"
        		,reportSuccess
        		,reportFailure
        		,10000  // 10 second timeout
        		,reportTimeout
        		);

        or specifying all possible callbacks and a timeout:

        	com.bristle.jslib.Ajax.Util.getAjaxXML
        		("my_servlet?p1=abc"
        		,reportSuccess
        		,reportFailure
        		,10000  // 10 second timeout
        		,reportTimeout
        		,reportInteractive
        		,reportLoaded
        		,reportLoading
        		,reportUninitialized
        		);

        Here is a Bristle Software JavaScript Library routine that does it:

        /******************************************************************************
        * Creates a new XMLHttpRequest object for use with Ajax operations, registers 
        * the specified functions as callbacks, and queues an asynchronous request to 
        * get XML from the specified URL.  The callback functions will be called 
        * asynchronously in response to the various state changes of the 
        * XMLHttpRequest as it processes the Ajax request.
        *
        * Notes:
        * - See registerAjaxCallbacksAndStartTimer for details of the callback 
        *   parameters, which are all optional.
        *
        *@param strURL              The URL to be accessed via Ajax
        *@param callbackSuccess     Function to be called when the state is Complete, 
        *                           and the HTTP status indicates success.
        *@param callbackFailure     Function to be called when the state is Complete, 
        *                           and the HTTP status indicates failure.
        *@param intTimeoutMillisecs Number of seconds before aborting the Ajax call
        *                           and calling callbackTimeout.
        *                           Optional.  Default=0 which means wait forever.
        *@param callbackTimeout     Function to be called if the Ajax call times out.
        *@param callbackInteractive Function to be called when the state is Interactive
        *@param callbackLoaded      Function to be called when the state is Loaded
        *@param callbackLoading     Function to be called when the state is Loading
        *@param callbackUninitialized
        *                           Function to be called when the state is Uninitialized
        *@throws com.bristle.jslib.Exception.intEXC_UNABLE_TO_CREATE_AJAX_OBJECT
        *@throws com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_OPEN
        *@throws com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_SEND
        ******************************************************************************/
        com.bristle.jslib.Ajax.Util.getAjaxXML =
        function(strURL
                ,callbackSuccess
                ,callbackFailure
                ,intTimeoutMillisecs
                ,callbackTimeout
                ,callbackInteractive
                ,callbackLoaded
                ,callbackLoading
                ,callbackUninitialized)
        {
        
            var xhr = com.bristle.jslib.Ajax.Util.createAjaxObject();
        
            // Try block to catch all errors so the timer can be cancelled.
            var timer = null;
            try
            {
                timer = com.bristle.jslib.Ajax.Util.registerAjaxCallbacksAndStartTimer
                    (xhr
                    ,callbackSuccess
                    ,callbackFailure
                    ,intTimeoutMillisecs
                    ,callbackTimeout
                    ,callbackInteractive
                    ,callbackLoaded
                    ,callbackLoading
                    ,callbackUninitialized
                    );
        
                // Queue the asynchronous HTTP request.
                try 
                {
                    var blnASYNC = true;
                    xhr.open("GET", strURL, blnASYNC);
                }
                catch (exception)
                {
                    throw new com.bristle.jslib.Exception.Exception
                            (com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_OPEN
                            ,"Error during open() of Ajax object"
                            ,"com.bristle.jslib.Ajax.Util.getAjaxXML"
                            );
                }
                try 
                {
                    xhr.send("");
                }
                catch (exception)
                {
                    throw new com.bristle.jslib.Exception.Exception
                            (com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_SEND
                            ,"Error during send() of Ajax object"
                            ,"com.bristle.jslib.Ajax.Util.getAjaxXML"
                            );
                }
            }
            catch (exception)
            {
                if (timer != null)
                {
                    window.clearTimeout(timer);
                }
                throw exception;
            }
        }

        --Fred

      4. Synchronous Ajax

        Original Version: 6/1/2008
        Last Updated: 8/6/2008
        Applies to:  JavaScript 1.0+, IE 5.5+, Firefox 1+, Netscape 7+

        Ajax is most commonly done asynchronously, so that the browser window doesn't freeze while waiting for the server.  However, for those times when you prefer simplicity, and can afford to briefly freeze the browser, synchronous Ajax is even easier.

        You can retrieve a string from the server, with a one-liner like:

                strData = com.bristle.jslib.Ajax.Util.doSynchronousAjax
                		('my_servlet?p1=abc').responseText;

        or to get an XML DOM that you can then manipulate:

                xmlDOM = com.bristle.jslib.Ajax.Util.doSynchronousAjax
                		('my_servlet?p1=abc').responseXML;

        or to check the success of the Ajax operation before manipulating the data:

                xhr = com.bristle.jslib.Ajax.Util.doSynchronousAjax
                		('my_servlet?p1=abc');
                if (xhr.status >= 200 && xhr.status <= 299)
                {
                    strData = xhr.responseText;
                }
                else
                {
                    alert("Ajax error: " + xhr.status + ": " + xhr.statusText);
                }

         

        Here is a Bristle Software JavaScript Library routine that does it:

        /******************************************************************************
        * Creates a new XMLHttpRequest object and uses it synchronously to make an
        * HTTP request.
        *@param strURL              The URL to be accessed via Ajax
        *@return The XMLHttpRequest object, so the caller can examine its properties:
        *           status, statusText, responseText, responseXML
        *@throws com.bristle.jslib.Exception.intEXC_UNABLE_TO_CREATE_AJAX_OBJECT
        *@throws com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_OPEN
        *@throws com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_SEND
        ******************************************************************************/
        com.bristle.jslib.Ajax.Util.doSynchronousAjax =
        function(strURL)
        {
            var xhr = com.bristle.jslib.Ajax.Util.createAjaxObject();
            try 
            {
                var blnASYNC = true;
                xhr.open("GET", strURL, !blnASYNC);    //?? Change to POST?
            }
            catch (exception)
            {
                throw new com.bristle.jslib.Exception.Exception
                        (com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_OPEN
                        ,"Error during open() of Ajax object"
                        ,"com.bristle.jslib.Ajax.Util.doSynchronousAjax"
                        );
            }
            try 
            {
                xhr.send("");
            }
            catch (exception)
            {
                throw new com.bristle.jslib.Exception.Exception
                        (com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_SEND
                        ,"Error during send() of Ajax object"
                        ,"com.bristle.jslib.Ajax.Util.doSynchronousAjax"
                        );
            }
            return xhr;
        }

        --Fred

    6. Loading Images

      1. Set the SRC property of the IMG

        Last Updated: 6/3/2008
        Applies to:  All browsers

        Dynamically loading images into a Web page is even easier than loading XML or text.  Simply assign a new URL to the SRC property of the IMG tag, as:

        	document.getElementById("img1").src = "image1.jpg";

        or, if you are generating the images with a servlet: 

        	document.getElementById("img1").src = "my_servlet?p1=abc";

        This makes sense because browsers were invented in the days of slow Internet connections, where images took a long time to load, and impatient users often clicked ahead on partially loaded pages to go to the next page.  Therefore, image loading has always been done in a separate thread from the main page load, and has always been dynamic.  The same is true for the SRC property of the IFRAME and SCRIPT tags, so you can do pseudo-Ajax of text via them.

        However, there are some "gotchas" to be aware of.  See the tips below.

        --Fred

      2. Prevent Browser Image Caching

        Original Version: 6/3/2008
        Last Updated: 6/4/2008
        Applies to:  All browsers

        Beware browser caching when image files may change on the server, or when you are dynamically generating images with a servlet.  If the new URL is identical to a URL that is already in the browser cache, the browser may reuse the old image.  This can happen despite any Cache-Control HTTP header set by the servlet, and despite any non-caching options the user may have set in the browser.

        To force the browser to go back to the server for a new image, include a dummy URL parameter with a unique value.  Using a simple JavaScript counter may not be sufficient:

        	var intCounter = 1;
        	function myFunction()
        	{
        	    document.getElementById("img1").src = "my_servlet?p1=abc"
        	            + "&forceReload=" + (intCounter++);
        	}

        Even if intCounter is a global JavaScript variable, it gets reset at each page load, so you may still get duplicates.  It is better (and easier) to use a random number as: 

        	document.getElementById("img1").src = "my_servlet?p1=abc"
        	        + "&forceReload=" + Math.random();

        However, there's still the slight chance of getting duplicate random numbers eventually, so you could use the current time in milliseconds as: 

        	document.getElementById("img1").src = "my_servlet?p1=abc"
        	        + "&forceReload=" + new Date().getTime();

        However, there's still the slight chance of generating multiple URLs in the same millisecond, and JavaScript doesn't yet have an equivalent to Java's System.nanoTime().  Besides, some day computers will be fast enough that you'd need picoTime(), or femtoTime(), or attoTime(), or zeptoTime(), or yoctoTime(), or...

        In cases where it really matters, it's probably best to use both, since you're VERY unlikely to get the same random number twice in the same millisecond: 

        	document.getElementById("img1").src = "my_servlet?p1=abc"
        	        + "&forceReload=" + Math.random()
        	        + "_" + new Date().getTime();

        Personally, I wrap it all up and make it self-documenting as: 

        	document.getElementById("img1").src = "my_servlet?p1=abc"
        	        + com.bristle.jslib.Ajax.Util.getForceReloadParam();

        Here is the Bristle Software JavaScript Library routine to generate the random URL param:

        /******************************************************************************
        * Get a unique URL parameter to force a reload, bypassing any cached pages.
        ******************************************************************************/
        com.bristle.jslib.Ajax.Util.getForceReloadParam =
        function()
        {
            // Generate a unique URL param to force a reload, bypassing the browser
            // cache.  This is especially useful for loading images via the SRC 
            // property of the IMG tag because otherwise, depending on the browser
            // and the user settings, the previously cached image may be re-used, 
            // despite any Cache-Control header that may be set.
            // Note:  Use a timestamp to avoid random duplicates.
            // Note:  Use a random number in case of multiple calls within the same
            //        millisecond.
            // Note:  Use getTime(), not getMillseconds() which returns a value 0-999.
            intRandom = com.bristle.jslib.Util.randomInt(1,1000000);
            return "&forceReload=" + intRandom + "_" + new Date().getTime();
        }

        and the Bristle Software JavaScript Library routine that it calls internally:

        /******************************************************************************
        * Generate a random integer between the specified min and max.
        *
        *@param intMin  Min acceptable random integer (inclusive)
        *@param intMax  Max acceptable random integer (inclusive)
        *@return        The random number
        ******************************************************************************/
        com.bristle.jslib.Util.randomInt =
        function(intMin, intMax)
        {
            if ((typeof(intMin) == "undefined") || (intMin == null))
            {
                intMin = 0;
            }
            if ((typeof(intMax) == "undefined") || (intMax == null))
            {
                intMax = Math.pow(2, 16) - 1;
            }
        
                                                        // Example with min 6 and max 10:
            var intRangeSize = intMax - intMin + 1;     // Range size = 5
            var intRandom = Math.random();              // Between 0 and 0.999...
            intRandom *= intRangeSize;                  // Between 0 and 4.999...
            intRandom = Math.floor(intRandom);          // Between 0 and 4
            intRandom += intMin;                        // Between 6 and 10
            return intRandom;
        }

        Thanks to John Ferguson and Matt Brophy for pushing me to bother including the timestamp, not just a random number!

        --Fred

      3. Beware <IMG SRC="">

        Last Updated: 6/3/2008
        Applies to:  All browsers

        To avoid mysterious page re-loads and mysterious execution of server side code, don't use the empty string ("" or '') as the value of SRC.  Instead, use the URL of an invisible GIF file, like this one, which you can right-click to download:  invisible.gif.  

        If you are planning to dynamically update the URL of an image immediately, you may not care what value is specified in the static HTML and may be tempted to specify the empty string as:

        	<img id='img1' src=''>

        Bad idea!  Use an invisible GIF instead, as: 

        	<img id='img1' src='invisible.gif'>

        Similarly, if you want to dynamically clear the image, you may be tempted to specify the empty string as: 

        	document.getElementById("img1").src = "";

        Don't do it!  Use an invisible GIF instead, as:

        	document.getElementById("img1").src = "invisible.gif";

        or better yet, dynamically set the IMG to invisible, as described in Hiding/showing HTML elements.

        Why?  2 reasons:

        In standards-compliant browsers like Firefox, the empty string maps to the URL (minus POST params) of the current page.  In non-standards-compliant browsers like Internet Explorer, it maps to the directory portion of the URL of the current page, which the server probably maps to a default page or servlet.  In either case, there are two problems:

        1. The URL returns an HTML page, not an image, so the browser displays its broken link image.
        2. Your server side code is being unexpectedly executed without any POST parameters, and may do screwy things, like re-running the most recent database transaction, re-adding a value to a shopping cart, etc.

        For more details, see Section 13.2 "Including an image: the IMG element" of the HTML spec:
            http://www.w3.org/TR/html401/struct/objects.html#h-13.2
        which defines the value of SRC as a standard URI, and Section 4.4 "Same-Document Reference" of the URI spec:
            http://tools.ietf.org/html/rfc3986#section-4.4
        which says that an empty string URI refers to the URI of the enclosing base document.

        --Fred

    7. Using Ajax for a Keep-Alive

      Last Updated: 12/13/2007
      Applies to:  JavaScript 1.0+, IE 5.5+, Firefox 1+, Netscape 7+

      OK.  So now, you're writing very sophisticated Web apps, using Ajax to go back to the server as needed, and presenting a very responsive and dynamic user interface.  You've put a large chunk of your application into a single Web page that interacts extensively with the user and collects lots of data.  But...

      What happens if the user enters a bunch of data and then goes to lunch for an hour without saving first?  Or spends a long time interacting with the fancy JavaScript parts of your app that collect data but don't need the server and so don't use Ajax?  When the user finally clicks Save or OK, it fails because the server session has timed out.  You've actually made things worse by taking the load off of the server and doing more locally.  How to prevent the timeouts?

      You could simply make the timeouts much longer, or disable them entirely.  However, the timeouts exist for a good reason.  They allow the server to discard the sessions of users who really are done with your app, and have closed the browser or gone to another site.  Without the timeouts, your server would waste lots of resources, and eventually crash for lack of memory.  Better to leave the timeouts in place.

      A better solution is to keep the sessions alive only for users who still have a Web page of your app open.  You can do this easily with a timer doing Ajax "keep-alive" operations to the server.  Ideally, you could just add a one-liner like the following to the onload event handler of each of your Web pages:

      	com.bristle.jslib.Ajax.Util.schedulePeriodicKeepAlive
      		("my_keep_alive_servlet"
      		,300000  // Every 5 minutes
      		);

      This would invoke your servlet every 5 minutes, ignoring any data it sent back, as well as ignoring whether it succeeded, failed, or timed out.  That's all you really need to prevent the session timeout.

      If you also want to process the returned data, detect Ajax failures and timeouts, warn the user that the server (or your communications to it) seems to be down temporarily, etc., you need additional callbacks to handle that, and might use:

      	com.bristle.jslib.Ajax.Util.schedulePeriodicKeepAlive
      		("my_keep_alive_servlet"
      		,300000  // Send keep-alive every 5 minutes
      		,!com.bristle.jslib.Ajax.Util.blnDO_FIRST_KEEP_ALIVE_IMMEDIATELY
      		,callbackSuccess
      		,callbackFailure
      		,30000	// Timeout each keep-alive after 30 seconds
      		,callbackTimeout
      		);

      Here is a Bristle Software JavaScript Library routine that does it:

      /******************************************************************************
      * Use Ajax operations to access the specified URL periodically to keep the 
      * connection to the server from timing out.  The specified callback functions
      * will be called asynchronously when the Ajax operation succeeds or fails.
      *
      * Notes:
      * - See registerAjaxCallbacksAndStartTimer for details of the callback 
      *   parameters, which are all optional.
      *
      *@param strURL          The URL to be accessed via Ajax
      *@param intMillisecs    Number of milliseconds to wait between accesses.
      *@param blnNow          Access the URL now, before the first delay, if true.
      *@param callbackSuccess Function to be called when the state is Complete, 
      *                       and the HTTP status indicates success.
      *@param callbackFailure Function to be called when the state is Complete, 
      *                       and the HTTP status indicates failure.
      *@param intTimeoutMillisecs 
      *                       Number of seconds before aborting the Ajax call
      *                       and calling callbackTimeout.
      *                       Optional.  Default=0 which means wait forever.
      *@param callbackTimeout Function to be called if the Ajax call times out.
      ******************************************************************************/
      com.bristle.jslib.Ajax.Util.blnDO_FIRST_KEEP_ALIVE_IMMEDIATELY = true;
      com.bristle.jslib.Ajax.Util.schedulePeriodicKeepAlive =
      function(strURL
              ,intMillisecs
              ,blnNow
              ,callbackSuccess
              ,callbackFailure
              ,intTimeoutMillisecs
              ,callbackTimeout
              )
      {
          // Schedule the next call to this function via setTimeout.
          // Note: Can get setTimeout to pass the arguments on the scheduled call,
          //       without having to stringize them into a call string, by creating 
          //       an anonymous function (closure) that calls the desired function
          //       passing the arguments, which are known via the closure at the 
          //       time the anonymous function is generated.
          var timer = window.setTimeout
                  (function() 
                   {
                       com.bristle.jslib.Ajax.Util.schedulePeriodicKeepAlive
                              (strURL
                              ,intMillisecs
                              ,true
                              ,callbackSuccess
                              ,callbackFailure
                              ,intTimeoutMillisecs
                              ,callbackTimeout
                              );
                   }
                  ,intMillisecs
                  ,"JavaScript"
                  );
      
          if (blnNow)
          {
              try
              {
                  com.bristle.jslib.Ajax.Util.getAjaxXML
                      (strURL
                      ,callbackSuccess
                      ,callbackFailure
                      ,intTimeoutMillisecs
                      ,callbackTimeout
                      );
              }
              catch(exception)
              {
                  // Report error to the user.
                  // Note: Seems like a bad idea for this library routine to show an
                  //       error to the user, instead of just throwing it to the 
                  //       caller.  However, after the first call has scheduled 
                  //       further calls here via setTimeout(), there is no caller
                  //       to throw to.  Any ideas?
                  //       Factors that make this more acceptable:
                  //       - This will only happen after the user has been idle for 
                  //         the full keep-alive timeout period, at which point he 
                  //         might well want a warning that he is approaching the 
                  //         server timeout.
                  //       - This only happens when we are unable to create the Ajax 
                  //         object, or when an error occurs on the call to open() or
                  //         send() to queue an asynchronous HTTP transaction.
                  //         It does not occur if an error occurs during the HTTP 
                  //         transaction.  That type of error is detected via the 
                  //         status field of the Ajax callback.  Therefore, this 
                  //         error is not caused by the unavailability of the server.
                  //         Only by the existence of support for the Ajax object in 
                  //         the current browser, the validity of the URL and its 
                  //         params, etc., including attempts to use a URL in a 
                  //         different domain than the current web app.  Such errors 
                  //         should be found and resolved by developers, not end 
                  //         users.
                  // Note: The following specific errors may occur:
                  //       - com.bristle.jslib.Exception.intEXC_UNABLE_TO_CREATE_AJAX_OBJECT
                  //         - When unable to even create the Ajax object, because it 
                  //           is not supported by this browser, or because the user 
                  //           disallowed it.        
                  //       - com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_OPEN
                  //         - When there's a problem with the URL, including the 
                  //           cross-domain security error.  In that case, Firefox 
                  //           always throws the error, and IE only throws it if the 
                  //           user answers Yes to the popup:
                  //                  "This page is accessing information that is not 
                  //                  under its control.  This poses a security risk.
                  //                  Do you want to continue?"
                  //       - com.bristle.jslib.Exception.intEXC_ERROR_DURING_AJAX_SEND
                  //         - When there's a problem with the URL parameters.
                  com.bristle.jslib.MsgBox.reportError
                      ("An error occurred in the Ajax object used for the keep-alive"
                       + " function to URL " + strURL
                       + "<br />"
                       + "This browser does not support Ajax, or you have set an"
                       + " option to disable it.  Therefore, nothing"
                       + " will prevent the web server from timing out if"
                       + " you are idle long enough."
                      ,exception
                      ,com.bristle.jslib.MsgBox.blnMSGBOX_SET_FOCUS
                      );
      
                  // Cancel further keep-alive attempts if we couldn't even create
                  // the Ajax object, and send the request.  This is not a problem
                  // with the server; it is a problem with doing Ajax at all in this
                  // browser.  Otherwise, errors will keep popping up on each 
                  // attempt.
                  //?? Also set a page variable that will go back at the next normal
                  //?? (non-Ajax) submit to set a flag in the server session to not
                  //?? do any more timeouts, or even no more Ajax, for the rest of
                  //?? the session.
                  window.clearTimeout(timer);
              }
          }
      }

      --Fred

    8. Using Ajax for Drill-down (Coming soon...)

      Last Updated: 12/5/2007
      Applies to:  JavaScript 1.0+

      Coming soon...

      --Fred

    9. Using Ajax for Infinite Scrolling of Long Tables (Coming soon...)

      Last Updated: 12/5/2007
      Applies to:  JavaScript 1.0+

      Coming soon...

      --Fred

    10. Using Ajax for Data Validation (Coming soon...)

      Last Updated: 12/5/2007
      Applies to:  JavaScript 1.0+

      Coming soon...

      --Fred

    11. Comet: Using Ajax for Server Push (Coming soon...)

      Last Updated: 12/18/2007
      Applies to:  JavaScript 1.0+

      Coming soon...

      --Fred

  6. RWD -- Responsive Web Design

    Original Version: 4/11/2012
    Last Updated: 2/7/2020
    Applies to:  JavaScript 1.0+

    Many of the techniques I've used since the year 2000 or so are now called "RWD (Responsive Web Design)".  Here's a quick demo I whipped up to show the concept to a client in 2012.

    You can play with it, dragging the window narrower and wider to see the effects.  Open it in your phone's browser (or into a browser window that is already narrow) to see it start out narrow.  Rotate your phone to see it widen, etc.  The same page looks good on a laptop, tablet or phone.

    You can also do a "View Source" in your laptop browser to see the JavaScript source code, and the JSON data structure that it uses to know when to change layouts, etc.

    I did this all with a few lines of JS, just to demo it to a colleague, and to show what it could do.  Most people these days are doing a simpler, less powerful form of RWD using strictly the new "@media query" capability of CSS.  So, they write no JS code at all, but can't get quite all the effects I can get.  One framework that has really popularized this CSS approach is "Twitter Bootstrap".

    Here's a link to the Horse Demo that does it all via CSS, but can't do quite as much as my JS approach.  Has some really cool effects.  Drag it wider and narrower.  Watch as the fonts change, buttons move from top to bottom, horse image gets trimmed to show just the head, etc:

    And here's a site I created professionally, using the same techniques:

    --Fred

  7. SPA -- Single Page App

    Original Version: 6/14/2000
    Last Updated: 2/8/2020
    Applies to:  JavaScript 1.0+

    SPAs (Single Page Apps) have become very common in recent years.  Here's how and why they came into existence.

    Client/server apps

    Client/server apps on PCs used to be pretty full-featured.  Lots of feedback as the user hovered over things, clicked on them, etc.

    But they had to be paid for, licensed, installed on each client PC, etc.  And of course, updated on each PC when bugs were found, upgraded on each PC when new versions were released with new features, etc. 

    And if you were unfortunate enough to be working with Microsoft Windows, you had all the "DLL Hell" issues, where installing or upgrading one app would break other apps.  So, no one ever wanted to risk a new install or an upgrade, unless they could afford to lose a few days, as they struggled to get their computer working again.  Therefore, you had to support many versions of the same app, fixing newly discovered bugs in all the different versions, until you could finally force everyone off of the older versions.

    Web apps

    Then came web apps, which required no install.  You could connect to a web server with any browser on any computer and run the app.  No licensing costs.  No DLLs or other shared libraries to be updated and maybe break other apps that used them.  In fact, you could safely walk into the CEO's office, give a quick demo and walk out.  If you got a call later to say something was broken on the CEO's computer, you could be SURE that you hadn't caused it.

    And everyone ran the same copy of the code, which was installed only on the central Web server, not on each client device.  So no coordination of updates/upgrades on all client devices.  Much less work!

    One problem with web apps was that they presented a much more simplistic UI.  You could interact with the form all you wanted, entering data into text boxes, selecting from drop down lists, etc.  But no feedback.  Nothing changed on the page until you finally clicked "Submit", at which point you finally learned that you'd made the same type of data entry mistake in a bunch of places.  Or gone a long way down some dead end route that the app was not going to allow.  Doh!!  Such web apps are now referred to as "Web 1.0".

    Ajax and SPAs

    As people started writing more JavaScript, and especially as they started using the technique that eventually came to be called "Ajax", web apps got to be more full-featured.  People started referring to them as "rich clients", as in a rich, full-bodied flavor, as opposed to the thin, watery, tepid flavor of the original Web apps.

    I've done an extreme job of that, starting with a project at GSK in the year 2000.  And in all my apps ever since.  I'd gotten a tip over lunch from my friend Ed Bacon about the possibility of using a new technique (now called Ajax). I was already using a lot of JavaScript and DHTML (Dynamic HTML), but I started doing more and more Ajax.

    Eventually, the HTML page initially loaded from a server was nothing more than a single <SCRIPT> tag that loaded a JavaScript file.  The JavaScript would use DHTML and Ajax to build the "real" HTML page for the user to see.

    The dynamically created page typically continues to be dynamic.  It updates itself at each keystroke, each click, sometimes each mouse move, going back to the server for more data via Ajax as needed, but rarely flushing the entire HTML page and loading a new one.  I started calling my extreme rich client techniques "filthy rich client".

    Nowadays, people doing the same call it "SPA (Single Page App)" or "Web 2.0".  An SPA offers all the power (rich UI) of a traditional client/server app with the "no install" advantage of a web app.  It's the best of both worlds!

    --Fred

  8. PWA -- Progressive Web App

    Original Version: 12/14/2018
    Last Updated: 2/11/2020
    Applies to:  EcmaScript ES6+ (maybe earlier?)

    PWAs (Progressive Web Apps) started to appear in 2015 or so.

    Before that, people used RWD (Responsive Web Design) to create a single web app that looked pretty good on different screen sizes (laptop, tablet, and phone).  They used CSS and/or JS to re-size the page elements, move them around, switch to phone-style buttons instead of regular web page buttons, etc.  So the same web app ran fine on all devices.

    They also used SPAs (Single Page Apps) to create an extremely full-featured "filthy rich client" UI, with lots of "Ajax" calls behind the scenes to update the web page as the user interacted with it.

    PWAs are SPAs, still using the same RWD and Ajax techniques to look good on all devices and to provide a rich UI.  As since SPAs are web apps, PWAs still require no install, and still run the same code base from the same central web server on all devices.

    But PWAs go a step further and take advantage of the features of the client they happen to be running on.  A PWA can cache code and data locally (on the phone, tablet or laptop) when you run it the first time from the web.  It can use that cached code and data to keep working when you have no Internet connection.  Data may become stale, but the app should still work.  It re-syncs and becomes current when you re-connect.

    Also, PWAs provide more access to device-specific features, like the camera and accelerometer of a phone or tablet, the contact list of a phone, etc.  And can install icons on a phone's home screen.

    So, PWAs provide all the advantages of RWD, Ajax and SPAs.  But also the device access and ability to work off-line of native mobile apps.  The best of both worlds!  And one of those worlds (SPA) was already the best of 2 other worlds (client/server apps and web apps).  So it's really the best of 3 worlds!

    Thanks to Jesse Bruckman for introducing me to the concept of a PWA over lunch in 2018!

    Thanks to Matt Brophy for this link to Google's checklist of features of a good PWA:

    --Fred

  9. Tools

    1. Debugging Tools

      1. View Source

        Original Version: 5/26/2007
        Last Updated: 6/29/2007
        Applies to:  JavaScript 1.0+

        One of the simplest tools for understanding and debugging Web pages and Web apps is the "View Source" capability built into all Web browsers.  It is usually available from the right click menu and from the View menu of the main menu bar.  It shows the HTML, JavaScript and CSS source code for the current page.  This is useful in a couple of situations:

        1. To learn HTML, JavaScript and CSS techniques.

          Since View Source shows the HTML, JavaScript and CSS source code, you can use it as learning tool.  Find a page somewhere on the Web that does what you are trying to do.  Use View Source to see how it does it, and use the same technique in your own code.  Be careful not to abuse copyright protections.  

          Be aware that other people all over the world can do this with your Web pages, so be sure to add copyright notices to your HTML, JavaScript and CSS code as appropriate.  Also, do not reveal any critical security info, sensitive customer info, trade secrets, etc. in such code and comments.  All such info should be wrapped inside the JSP or Java servlet code or comments, or better yet, in the Java beans and business objects accessed by the JSP and servlets.

        2. To understand and debug your JSP and servlet code.

          View Source doesn't show your JSP and servlet code, which executes on the server, but it does show the output of that code.

          If you use lots of JSP @include directives (see http://bristle.com/Tips/Java.htm#jsp_directives) or jsp:forward, jsp:include, and jsp:invoke actions (see http://bristle.com/Tips/Java.htm#jsp_and_jstl_actions), View Source is a useful way to see the actual "flattened" output that results from all of the including, invoking, and forwarding.  Also, if you have complex JSP or servlet logic to determine what HTML, JavaScript and CSS to generate, View Source is a useful way to see the results of that logic.

          Keep in mind that JSP comments and Java servlet comments are not sent to the browser, and so are not visible in View Source, but HTML, JavaScript and CSS comments are.  Therefore, don't put information into HTML, JavaScript and CSS comments that you don't want the end user to be able to see by doing a View Source.  Do put information into them for temporary debugging purposes (a technique similar to printing to standard output).  Also, do put information into them (permanently, even in production code) that you don't want to show on the HTML page, but that might be useful to you for debugging production code, as long as you don't mind the occasional inquisitive user being able to peek at it.

        3. To resolve caching issues.

          If your latest changes to the JSP, HTML, JavaScript or CSS code have had no effect, view the source to see whether you are still using an old cached version of the file without those changes.

          The browser caches files to avoid going to the server for the same page over and over, but you can force it to get the latest file by clicking the Refresh or Reload button, especially if you hold down the Shift key while doing so.  You can also explicitly delete the contents of the browser cache.  In some browsers, you can change a user preference that controls how often the browser checks for newer versions of files it has cached.

          The server may also cache pages, especially pages generated by JSP or servlets.  You may need to set the server to check more aggressively for updated JSP source files, recompiling them into Java source files and then into Java class files.  Also, to check more aggressively for updated Java class files, reloading them, instead of using the copy already loaded into memory.  For tips on how to do this, see:

                  http://bristle.com/Tips/Tomcat.htm#enable_servlet_reload
                  http://bristle.com/Tips/Tomcat.htm#enable_default_servlet_reload
                  http://bristle.com/Tips/Tomcat.htm#servlet_reload_frequency
                  http://bristle.com/Tips/WebLogic.htm#servlet_reload_frequency
                  http://bristle.com/Tips/WebLogic.htm#jsp_reload_frequency

          You may also need to specify in your JSP and servlet code that the browser should not cache the generated pages.  For tips on how to do this, see:

                  http://www.mnot.net/cache_docs/

          In any case, View Source is an easy way to detect caching because you can see the HTML, JavaScript, and CSS source code and notice that your changes are missing.

        --Fred

      2. View Source of Referenced JavaScript and CSS Files

        Last Updated: 5/26/2007
        Applies to:  JavaScript 1.0+

        View Source can show you not only the source of the main HTML page, but also the source of any referenced JavaScript and CSS pages.  

        Embedded in the code shown by View Source, you may see lines like:

                <script language='JavaScript' src='js/com.bristle.jslib.Util.js'></script>
                <link rel='stylesheet' type='text/css' href='css/com_bristle_jslib_css_Styles.css'>

        These are references to JavaScript and CSS source files.  If you copy the relative pathnames of the files:

                js/com.bristle.jslib.Util.js
                css/com_bristle_jslib_css_Styles.css

        and paste them into the address bar of your browser in place of the main HTML or JSP file name, you can directly access the referenced file and then do a View Source on it.  For example, if the main URL shown in the address bar is:

                http://bristle.com/blah/blah/file1.htm

        Change it to one of:

                http://bristle.com/blah/blah/js/com.bristle.jslib.Util.js
                http://bristle.com/blah/blah/css/com_bristle_jslib_css_Styles.css

        Again, be careful not to abuse copyright protections, and since other people can do this with your Web pages, be sure to protect your own code as appropriate.

        --Fred

      3. Firefox View Live Source

        Original Version: 4/9/2007
        Last Updated: 9/13/2009
        Applies to:  Firefox 1.0+

        You may have noticed that View Source in a browser shows only the static HTML source that was sent from the Web server.  To see the "live source" that reflects any changes you made via DHTML (Dynamic HTML), in Firefox:

        1. Select the region of the page you want to examine by dragging the mouse (or hit Ctrl-A to select the entire page).
        2. Right click on the selected region, and choose "View Selection Source".

        Note that the "live source" represents the HTML DOM managed internally by Firefox, in its canonical form, not necessarily the textual form of the static page with your dynamic changes.  This is useful because the canonical form is an accurate depiction of what Firefox is actually rendering, so it is good for understanding the appearance and behavior of the rendered page.  However, it may differ from what you expect in the following ways:

        1. May add a TBODY element to wrap the TR elements in each TABLE element, even though you didn't bother to explicitly create a TBODY.
        2. May add missing end tags for TR, FONT, etc., if you accidentally left them off.
        3. May reformat the HTML text.  It generally preserves comments, indentation, and whitespace, but seems to delete some blank lines, and to remove line breaks and whitespace among the attributes of an HTML element.
        4. May re-order the attributes of an HTML element, alphabetizing them.
        5. etc.

        Thanks to Erin Mulder for "tipping me off" to "View Selection Source", and to Tim Stone for pointing out that it is canonicalized!

        BTW, if you're not already using the Firefox web browser, give it a try.  It's a free download from:
            http://www.mozilla.org/

        For tips on using Firefox, see:

            http://bristle.com/Tips/Internet.htm#mozilla_firefox

        Thanks to Jeff LoBello for pointing out that you can view the live source in Internet Explorer via the DHTML Spy add-on, available free for non-commercial use at:

            http://www.castapoint.com/

        --Fred

      4. Bristle BrowserWidth tool

        Original Version: 11/11/2012
        Last Updated: 2/22/2013
        Applies to:  JavaScript 1.0+

        Here's a simple Web page that shows the current width of your browser window:

        Use it to measure the width of your computer, tablet, or phone screen.  Or rotate the tablet or phone to measure the height.  Or drag the browser width to a different size to measure that size.

        --Fred

      5. Bristle JavaScriptEnabled tool

        Original Version: 2/22/2013
        Last Updated: 2/22/2013
        Applies to:  JavaScript 1.0+

        Here's a simple Web page that shows whether JavaScript is currently enabled in your browser:

        Use it from any desktop, laptop, tablet, or phone Web browser to see the setting for that browser.

        --Fred

      6. Bristle Color Picker

        Original Version: 12/13/2012
        Last Updated: 12/13/2012
        Applies to:  JavaScript 1.0+

        Here's a simple color picker tool that shows what font, background, and border colors look like in a Web browser:

        Not as powerful as many color pickers, and you may already get this ability and more from Firebug or Chrome Developer tools, but it's lightweight and works from any desktop, laptop, tablet, or phone Web browser (even Internet Explorer, for those unlucky few of you who still use it).

        --Fred

    2. Code Generation Tools

      1. Compass and Sass

        Original Version: 4/12/2012
        Last Updated: 4/12/2012
        Applies to:  JavaScript 1.0+

        Sass is a tool that generates low-level CSS from a higher level language, and Compass is a tool that builds further on Sass.  Both are free downloads, and very useful.  I've used them for a couple of years.

        Here are my notes from David Kaneda's "Abstracting CSS for Sustainable UI" talk at Philly ETE 2012:

        David Kaneda, Creative Director at Sencha, Wrote jQTouch
        
        - Abstract:
          David Kaneda will cover the benefits of abstracting CSS by
          taking a deep look at Sass and Compass. The presentation will
          include general benefits of abstracted CSS as well as a
          live-coding demo to show, first-hand, how to get started.
        
        - He's going to focus on SASS (Ruby) and Compass (Ruby)
        - Competition: "less" (JS), and "Stylus" (JS), pretty much the same but he 
          won't focus on them.
        - php : html :: saas: css (... is to ... as ... is to ...)
        - Install Ruby if you're on Windows (it's already on Mac) and then do 
          the following:
          % sudo gem install compass
        % compass init
        % compass compile
        % compass watch (compiles each time the file changes)
        - Generates all of the prefixes (-webkit-, -moz-, -ms-, -o-, etc.)
        - Superset of CSS, so you can just rename *.css to *.scss to get started
        - Features of Sass:
          1. Variables
             - $button_color: #0b589c;
          2. Functions
             - darken($button-color, 10)
          3. Nesting
             - Example: Can nest "span" CSS rule inside of "button" CSS rule 
               .button {
                  span {
                     color: red;
                  }
               }
          4. Mixins and CSS3
             - @include background(
                  linear-gradient(
                     top, 
                     lighten($button-color, 5)
                     darken($button-color, 5)
                  )
               )
             - Write your mixin -- function that emits raw CSS
               - @mixin buttonize($button-color: #d35606) {
                    ... Compass stuff here...
                 }
               - .button2 {
                    @include buttonize
                 }
             - Problem w/Mixins:  Leads to bloat.  Instead of inheriting CSS rules
               as you would normally, you end up generating duplicate CSS rules.
          5. @extend
             - Instead of generating more CSS rules just generate more selectors
               on same set of rules
             - .base-button {
                   font-size: 100px;
                   etc..
               }
             - .button2 {
                  @extend base-button
               }
        - Generates browser-portable CSS
        - @if $pill-shape {
          } @else {
          }
        - @return if(lightness($color) > 30, darken($color, 50), lighten($color, 50))
          - Make text lighter if background is darker
        - @include
        - @import 'compass', 'buttonize';
          - Imports files _compass.scss and _buttonize.scss
          - Underscores prevent them from generating CSS files of their own.  
        - Since default value of param can be a variable, you can override the 
          default outside of the mixin
        - He has a project:  http://moox.github.com/compass-recipes
          - Collection of Compass mixins for us to use
                    

        --Fred

      2. CoffeeScript (coming soon...)

        Original Version: 4/12/2012
        Last Updated: 4/12/2012
        Applies to:  JavaScript 1.0+

        Coming soon...

        --Fred

    3. Testing Tools

      1. SauceLabs.com

        Original Version: 3/14/2013
        Last Updated: 3/15/2013
        Applies to:  JavaScript 1.0+

        I looked into http://SauceLabs.com today, as a way to do manual and automated testing on multiple device/OS/browser combinations.

        Looks very promising:

        • Supports 135 device/browser/OS combinations
        • Free trial. Then cheap $12/month.
        • Manual testing via remote access to their browser
        • Video and screenshots of manual tests.
        • "Live Share" to screen share with someone while testing.
        • I tested the Django site via Android, iOS, IE9, Firefox, Chrome
        • Also tested my RWD fred.html page
        • Can install and run Firebug and other dev tools in remote browsers.

        Here are my raw notes:

        - Followed up on tip about saucelabs.com from Hunter of Empathy Labs.
        - https://saucelabs.com
          - Supports Selenium and WebDriver
        - https://saucelabs.com/pricing
          - $12/month (Mild) - $300+/month (Extra Hot)
          - Test on:  Windows, Linux, Android, Mac, iOS
        - https://saucelabs.com/docs/browsers
          - https://saucelabs.com/docs/browsers/se2
            - 118 Browser/OS via WebDriver, Python/Ruby/Node.js/Java/C#/PHP
          - https://saucelabs.com/docs/browsers/se1
            - 126 Browser/OS via Selenium, JSON
            - Opera 9-12
            - IE 6-10
            - Win XP, 7, 8 (no Vista) 
            - Firefox 2-20
            - Safari 3-6
            - Chrome 24
            - iPad 4-6
            - iPhone 4-6
            - Android 4
            - Linux
          - https://saucelabs.com/docs/browsers/condensed
            - Nice concise summary of combinations
            - 135 Browser/OS
          - https://saucelabs.com/docs/additional-config#selenium-version
            - Selenium 2.0-2.31
          - Jenkins integration
          - Rest API
          - JS Unit tests
        - https://saucelabs.com/docs/manual
          - Manual testing:
            - Remotely drive browsers that are accessing your site
            - Manual tests are recorded for later replay as automated tests
            - 20 minute session limit (to contain costs)
            - Connection to server is from your browser, not the remote one?
              So you can access servers inside your firewall.
              - https://saucelabs.com/docs/connect
          - Record videos and screenshots of errors
        - https://saucelabs.com/
          - Tried it out.
          - Manual testing
            - Spins up a VM for me.
            - Go to http://bristle.com/hhlweb/mobile
            - It defaults to Mac Safari 5.0.  Can switch to other OS/browser.
            - Can click OS in previous screen to spin up Linux instead
          - Created an account:
            - Different prices for:
              - ?? minutes automated Win/Linux/Android
              - ?? minutes Mac/iOS
              - ?? parallel VMs
              - ?? minutes manual testing
            - $  0/month = Free (100, 40, 1, 30)
            - $  0/month = For testing OSS S/W (unlimited, unlimited, 3, ??)
            - $ 12/month = Mild (200, 80, 4, unlimited)
            - $ 49/month = Medium (1000, 400, 10, unlimited)
            - $149/month = Very (4000, 1600, 20, unlimited)
            - $279/month = Extra (8000, 3200, 30, unlimited)
          - Chose free account for now.
          - Has "Live Share" to screen share with someone while testing.  Nice!
          - Tested http://bristle.com/hhlweb/mobile via:
            - Android:
              - Brings up standard Android emulator
              - A little slow and clunky, but not bad.
              - Can scroll, long press, etc.
              - Can rotate the phone via Ctrl-F12
              - User agent string in server log is:
                  Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; 
                  Android SDK built for x86 Build/IMM76D) 
                  AppleWebKit/534.30 (KHTML, like Gecko) 
                  Version/4.0 Mobile Safari/534.30
            - Windows IE 9:
              - Looks right.  Same as I've seen on real Windows machines.
                No autosizing of fields, thinks it is narrow, etc.
              - Can scroll, drag window to new size (have to un-maximize first)
              - Arrow keys delete placeholder
              - Precise mouse positioning (to drag edge of window) is clunky
              - User agent string in server log is:
                  Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
            - iOS 6 iPad:
              - Brings up "iOS Simulator - iPad / iOS 6.0 (10A403)"
              - A little slow and clunky, but not bad.
              - Can tab, can drag to scroll
              - Can rotate the iPad via menu:  Hardwre | Rotate Left
              - User agent string in server log is:
                  Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) 
                  AppleWebKit/536.26 (KHTML, like Gecko) 
                  Version/6.0 Mobile/10A403 Safari/8536.25
          - Tested http://bristle.com/static/hhl/js/fred.html (RWD) via:
            - iOS 6 iPad:
              - Looks good.  Rotated.  Looks good.
            - Android:
              - Looks good.  Rotated.  Looks good.
            - Windows IE 9:
              - Looks good.  Dragged window size.  Looks good.
          - Upgraded to Mild ($12) account.
          - Tested http://bristle.com/static/hhl/js/fred.html (RWD) via:
            - Mac Firefox 19
              - Looks good.  Dragged window size.  Looks good.
              - Can install and run Firebug.
          - Can logout and back in and session is still running w/same browser
            status.  How to end a session w/o creating a new one?  Couldn't find
            a way, other than letting it timeout.
            - Asked via support forum.  They should e-mail me an answer.
            - Only way I've found so far:
              - Start job
              - Click username
              - Click link to see job NOT in fullscreen mode
              - Click "Cancel Job"
              - Shows it as an error though, not a clean exit.
            - Looks like they timeout within a couple minutes anyhow, so don't
              bother ending them.
            - [3/15/2013]
              There are VCR style Play (triangle) and Stop (square) buttons in 
              the top left toolbar.  Stop returns you to the list where the 
              test is still shown as "Running", but it ends soon.
          - Can have multiple sessions at once, each running a different browser,
            each in a different saucelabs tab.
        - How to automate tests?  Can you record a manual test and play it back
          as an automated test?  
          - Yes.  See the SauceLabs blog at: http://sauceio.com/
        - [3/15/2013]
          Move mouse off the top of the remote screen to see the menu on Mac.
        

        --Fred

      2. DEV, TEST and PROD Bookmarklets

        Original Version: 11/11/2014
        Last Updated: 11/12/2014
        Applies to:  JavaScript 1.0+

        Want a quick way to flip back and forth between DEV, TEST, and PROD versions of the web site you are working on?

        You've probably heard of applets and servlets.  What about "bookmarklets"?  They are small snippets of JavaScript code that run inside a URL.  As you may already know, a URL can start with not only "http:" or "https:", but also "mailto:", "ftp:", "about:", and many other protocols.  Another useful option is "javascript:", which is followed by a snippet of JavaScript code.

        Today I found a really useful tip at:

        It shows how to create bookmarklets to go to the exact same URL you are currently viewing but with the hostname changed to your DEV, TEST, or PROD server.  Nice!  I now have DEV, TEST, and PROD buttons in my Bookmark toolbar.  For example, If I'm at:

        • https://m.helphopelive.org/campaign/12

        and click TEST, it takes me directly to:

        • http://webtest1.helphopelive.org/campaign/12

        If I then click DEV, it takes me directly to:

        • http://localhost:8000/campaign/12

        Let me know if the instructions at the RevSys site are not sufficient.  I can add more detail here and/or contact Frank Wiles to update his tip at RevSys.

        --Fred

    4. HTML5

      1. Intro to HTML5 (coming soon...)

        Last Updated: 4/12/2012
        Applies to:  JavaScript 1.0+, HTML5+

        Coming soon...

        --Fred

      2. HTML5 "form" attribute of various input fields

        Last Updated: 11/26/2014
        Applies to:  HTML5+

        HTML5 includes a new "form" attribute on the various input elements, so you can specify which form should POST the data without having to nest the element inside the <form> tag.  See:

        Applies to these input elements

        • <button>
        • <fieldset>
        • <input>
        • <keygen>
        • <label>
        • <meter>
        • <object>
        • <output>
        • <progress>
        • <select>
        • <textarea>

        According to the W3Schools site, it works for Firefox, Chrome, Safari, and Opera, but not yet for MSIE:

        --Fred

      3. HTML5 Device APIs

        Last Updated: 4/12/2012
        Applies to:  JavaScript 1.0+, HTML5+

        Here are the slides from Brian Leroux's "Just Beyond HTML5: Device APIs with PhoneGap" talk at Philly ETE 2012:

        and the video:

        and here are my notes:

        Brian Leroux, Phone Gap Hacker, @BrianLeroux
        
        - Abstract:
          PhoneGap hacker Brian LeRoux will talk about new breed of cross
          platform capabilities from the W3C called Device APIs, their
          relationship to HTML5, web standards, WebKit and mobile
          development with the most popular platforms. If you are
          interested in mobile web dev for iOS, Android, BlackBerry and
          Windows Phone you'll really enjoy this talk. You can expect live
          code, remote debugging and cloud compilation too.
        
        - History of telecomm: telegraph, phone, radio, movies, TV, WWW, mobile
          - All turned into heavily regulated monopolies
        - Web is no longer just docs, URLs, links.
          - Now richer content, more interactive apps
        - Talk is not about PhoneGap, it's about device APIs
        - HTML5 will be followed by device APIs
        - Bought by Adobe recently
        - Canadian, moved to San Francisco
        - HTML5 is from WAT -- Web Application Technology working group
        - Now politicized and meaningless:  HTML5 means "new shiny"
        - HTML5
          - Looser than XHTML:
            - No quotes required for attributes
            - No close tags required
            - Parser more rigid, so it can tolerate these omissions
          - Structural elements:
            - <section>, <article>, etc.
          - Text level elements:
            - <time>, <code>, etc.
          - Form elements:
            - <progress>, <keygen>, etc.
          - Embedding elements:
            - <canvas>, <audio>, <video>, etc.
          - <input type = color, date, datetime, email, month, number, range
                          search, tel, time, url, week>
          - Programmatic APIS
            - drag/drop
            - web workers
            - server sent events
            - sockets
            - cross doc messaging
            - channel messaging
          - Off-line happens:
            - navigator.offLine
            - appcache
            - web storage (use via his product "lawnchair")
            - websql (indexeddb is better)
            - indexeddb (store JSON in browser)
            - various file APIs
        - Device APIs
          - He groups them into 3 groups:
            - Sensors
            - Data
            - Outputs
          - Organizational Forces:
            - WAC, Webinos (formerly BONDI?)
            - W3C DAP and Widgets
              - 2009
            - Mozilla WebAPI
            - Apache Cordova (new name for PhoneGap)
          - Related concept: "Carrier APIs"
            - Billing, messaging and location services
            - Tied to one carrier.
            - Carrier charges the user for use of the services
          - Related concept: "Installable Web Apps"
            - W3C widgets
            - Chrome Web Store
            - Mozilla open web apps
            - PhoneGap/Build
            - Suggests these implications:
              - Distribution model
              - Runtime security model
              - Self hosted, federated
          - Geolocation API
                var win = function(position) { console.log(position.coords) }
                ,   fail = console.log
                navigator.geolocation.getCurrentPostion(win, fail)
          - Vibration API
                navigator.vibrate(1000)
                navigator.vibrate([1000])
                navigator.vibrate([1000, 500, 2000])
          - Accelerometer API
               window.ondevicemotion = function(event) {
                    event.accelerationIncludingGravity.x
                    event.accelerationIncludingGravity.y
                    event.accelerationIncludingGravity.z
                }
          - Gyroscope API
                window.ondeviceorientation = function(event) {
                    event.alphar
                    event.beta
                    event.gamma
                }
          - Magnometer API
          - Media Sensors (aka Media Capture, aka Camera)
            - Camera, microphone
            - <input type=file accept=image/* capture=camera>
            - Valid types inc camcorder, microphone, filesystem.
                var getMedia = navigator.webkitGetUserMedia 
                             || navigator.mozGetUserMedia 
                             || navigator.msGetUserMedia
          - WebRTC ("he's hot on it")
            - Video conferencing through Web API
            - Emerging standard, even Microsoft is on board
          - Connecton API
            - navigator.onLine on steroids
            - Query actual bandwidth speed
            - Allowing introspection of connection property such as wifi, 3g, 
              etc. In PhoneGap:
                var networkState = navigator.network.connection.type;
                var states = {};
                states[Connection.UNKNOWN]  = 'Unknown connection';
                states[Connection.ETHERNET] = 'Ethernet connection';
                states[Connection.WIFI]     = 'WiFi connection';
                states[Connection.CELL_2G]  = 'Cell 2G connection';
                states[Connection.CELL_3G]  = 'Cell 3G connection';
                states[Connection.CELL_4G]  = 'Cell 4G connection';
                states[Connection.NONE]     = 'No network connection';
                alert('Connection type: ' + states[networkState]);
          - Battery Status API
            - Irony: this API will drain your battery.
            - Available in an earlier form in the PhoneGap project and now
              implemented in a rudimentary form in B2G. Combined with
              connection intellegent backup/syncronization
              possible.
                navigator.battery.addEventListener
                            ('dischargingtimechange', function () {
                    if (navigator.battery.dischargingTime < 60 * 10 
                    || navigator.battery.level < 0.05)
                        console.log('oh shiii, battery level below 5%!')
                    }
                }, false)
            - Note: currently an older API avail in PhoneGap.
          - App LifeCycle Events
            - Is all in background, resumed, etc
            - Currently understandardized. BEWARE! But clearly a need; exists 
              today in Chrome and PhoneGap in some forms.
                document.addEventListener('deviceready', readyFreddy, false)
                document.addEventListener('pause', justChillin, false)
                document.addEventListener('resume', woahDude, false)
                var readyFreddy = function() {
                    alert('app initd')
                }
                var justChillin = function() {
                    alert('paused')
                }
                var woahDude = function() {
                    alert('alright alright getting up!')
                }
          - Contacts API
            - Read/write contacts from app
                function win(contacts) {
                    alert('Found ' + contacts.length + ' contacts.')
                }
                function fail(contactError) {
                    alert('onError!')
                }
                var optns = new ContactFindOptions()
                opts.filter = 'Brian'
                var fields = ['displayName', 'name']
                navigator.contacts.find(fields, win, fail, opts);
          - File System API
            - window.requestFileSystem prompts user
                window.requestFileSystem
                            (LocalFileSystem.PERSISTENT, 0, gotFS, fail);
                function gotFS(fileSystem) {
                    var opts = {create: true, exclusive: false}
                    fileSystem.root.getFile
                                    ('readme.txt', opts, gotFileEntry, fail);
                }
                function gotFileEntry(fileEntry) { 
                    fileEntry.createWriter(gotFileWriter, fail) 
                }
                function gotFileWriter(writer) {
                    writer.onwriteend = function(evt) {
                        console.log("contents of file now 'some sample text'");
                        writer.truncate(11);  
                        writer.onwriteend = function(evt) {
                            console.log("contents of file now 'some sample'");
                            writer.seek(4);
                            writer.write(" different text");
                            writer.onwriteend = function(evt){
                                console.log
                                  ("contents of file now 'some different text'");
                            }
                        };
                    };
                    writer.write("some sample text");
                }
                function fail(error) { console.log(error.code) }
        - Boot2Gecko phone (hardware by Mozilla?, says Google on the back?)
        - WebAPI
          - Web Telephony API
          - Web SMS API
          - Screen Orientation API
          - Settings API
          - Resource Lock API
          - Power Management API
          - Mobile Connection API 
          - Sensor API (proximity and ambient light)
          - And WAY MORE: nfc, bluetooth, alarm, clock, intents, 
            capabilities, spellcheck, keyboard 
          - Honorable mention: Palm's webOS
        - PhoneGap
          - Now Apache Cordova
        - Concerns:
          - turn on the front facing camera and look at you
          - and email a picture of it to all your friends
          - backup data before your battery dies; without prompting
          - syncronize data to a service, optimally
          - know what building you are likely in, what floor you are probably 
            on, and the direction you are travelling 
        - Challenges:
          - Security
          - Privacy
          - Permissions
          - Capabilities
          - Performance
        - Build Web app first, then PhoneGap app if needed, then native app 
          if needed
        
                

        --Fred

    5. Pjax

      1. Intro to Pjax

        Original Version: 4/12/2012
        Last Updated: 4/25/2012
        Applies to:  JavaScript 1.0+, HTML5+

        Like Ajax, Pjax is a technique, not a programming language or any other new technology.  It requires no additional software.

        Also like Ajax, the Pjax technique can be used in any Web application, regardless of whether the server-side code is written in Java, .NET, Ruby on Rails, PHP, Perl, C/C++, or whatever.  Automatic support for it is being added to various Web frameworks.

        "Pjax" stands for "Ajax" plus "pushState".  It is a technique for loading Web pages faster -- an optimization, a form of caching.   Instead of loading an entire new Web page from a URL, Pjax uses Ajax to load only those portions of the new page that differ from the current page.  It also uses the new HTML5 history.pushState() method to update the browser's URL to that of the new page.  The result is the same as if the entire new page has been loaded.

        If the new page is similar enough to the old page, this can use much less network I/O and can use much less CPU time in the browser (not having to re-render the entire page).  However, like all caching techniques, it may use more CPU time.  Pjax uses CPU time on the server to load the old page and the new page, compare them, and generate a set of deltas.  It may also use more CPU time in the browser, to apply the deltas to the old page, and update the URL.

        Pjax is still very new.  You can write custom code to use the Pjax technique in your Web application, but there are some tough problems to solve before it can be used automatically by a framework:

        1. What if the old page was changed locally in the browser via JavaScript after it was loaded?  How would the server know, and be able to generate valid deltas?

        2. What if the old page was changed on its server after it was loaded into the browser?  How would the server of the new page (which may not be the same server) know, and be able to generate valid deltas?

        3. What if the new server is unable to access the old server due to firewall settings, or for any other reason?

        4. Pjax makes sense for workflows that involve loading a series of very similar pages from a single Web app, but what if that's not the case?  What if the old page is nothing like the new page?  It could be less efficient to apply the deltas than to just load the new page.

        Here are my notes from Adrian Holovaty's "Pjax and the next generation of server-side Web frameworks" talk at Philly ETE 2012:

        Adrian Holovaty, Co-Creator, Django
        
        - Abstract:
          Django co-creator Adrian Holovaty shares his ideas on the future
          of server-side Web frameworks. Are the current batch of
          frameworks getting long in the tooth? Should we really still be
          generating HTML from scratch on each page request? Can we
          automate some of the new best practices, such as Pjax?  No
          Django knowledge is necessary to understand this talk; the
          thoughts apply equally to Ruby on Rails and other circa-2005
          frameworks.
        
        - PJAX = Ajax + pushstate
          - Change URL to reflect that you're effectively at a different page
            even though you didn't reload the entire page.
          - Each page load just loads the parts that should change
            - deltas, diffs, patchs, etc.
          - GitHub uses it in tree view
          - jquery-pjax at GitHub
          - jQuery function:  .pjax()
          - HTTP header: HTTP_X_PJAX says that it is a pjax delta page
          - @pjax()
          - Need to still do, which he hopes to do in his new project 
            auto-pjax (currently vaporware)
            - Generate Web pages vs diffs
            - Multiple content boxes
            - What if link changes, but content does not?
            - Respect permalinks
            - Respect hashbangs (#!)
            - Don't require JS on initial page load
            - Bail if needed (when pages are too different to bother with diffs)
            - Security (hide app-specific internal data)
            - Favor correctness over performance 
              - Never serve up wrong or corrupted page when doing a 
                "Powered by Pjax" page
            - Amaze people
          - Server-side processing required by pjax:
            - Server does diff of the page you say you are currently at against
              page you're trying to load.  Sends only deltas.
          - Client-side processing required by pjax:
            - Browser receives deltas (and knows it because of the HTTP_X_PJAX
              HTTP header), and applies the deltas locally
                

        See also:

        --Fred

    6. Mobile Web

      1. Intro to Mobile Web (coming soon...)

        Last Updated: 4/12/2012
        Applies to:  JavaScript 1.0+

        Coming soon...

        --Fred

      2. Responsive Web Design (coming soon...)

        Last Updated: 4/12/2012
        Applies to:  JavaScript 1.0+, HTML5+

        Coming soon...

        --Fred

    7. CMS -- Content Management Systems

      1. Intro to CMS Systems

        Last Updated: 9/26/2011
        Applies to:  JavaScript 1.0+

        Trying to decide whether to use a CMS system like Drupal, Joomla, or WordPress, vs. hand-crafting a custom system?

        I've done most of my Web development using hand-crafted JavaScript and Ajax, and lately leveraging libraries like jQuery.  On the server, I use Java/Groovy/Grails, Tomcat, Apache, MySQL (or MongoDB), Spring, Hibernate, etc.  This gives me the power, flexibility, and execution speed I need for high-end web applications.

        However, for many small commercial Web sites, this is overkill.  I was recently impressed by how quickly a co-worker, Chris Hunter, was able to duplicate the look and feel of our web app with a simple Drupal web site.  He did not try to mimic any of our complex business logic, but was very quickly able to whip up a Web site with all of the same cosmetics as our app.  It allowed our users to register, participate in forums and surveys, use our knowledge base, etc., with deep links from the app directly into the help pages at the Web site.  He and I agreed that it would be a bad idea to try writing the entire app in Drupal, but it made a great addition to the app.

        For a large and growing number of apps, there may be no need for any custom code.  The capabilities of CMS systems like Drupal, Joomla, and WordPress are growing quickly, as are the thousands of modules, add-ons, and plugins that work with them.  There is no longer any need to hand-craft any of the standard stuff that most commercial Web sites require: forums, blogs, mailing lists, registration, shopping carts, catalogs, etc.

        Furthermore, CMS systems have always focused on the ability of non-technical users to manage the content of the Web site.  Thus, the company owner can easily update the mission statement, logo, and other text and graphics on the pages.  A sales manager can update prices, pictures, and descriptions of items, can organize groups of items into multiple linked pages, and can start and end special deals and promotions, etc.  Once you, as a Web programmer, have created a CMS-based Web site for a client, the client may be able to do all future support, maintenance, and upgrades of content, without any further help from you.

        For more info, see:

        --Fred

    8. Dart -- Google's JavaScript replacement

      Original Version: 2/12/2013
      Last Updated: 2/12/2013
      Applies to:  JavaScript 1.0+

      I've always wanted a more rigorously type-checked version of JavaScript.

      As a long-time Java programmer, I've wished for a version of Java that runs in the browser with full access to the HTML DOM like JavaScript has, not limited like Java applets.  Recently, Google is pushing "Dart", which is a step in that direction.

      I don't know whether even Google can overcome the popularity of JavaScript and the huge volume of existing JavaScript code, and I don't relish the idea of re-writing all of my own JavaScript code if Dart were to prevail and JavaScript to wither.  But, Dart does seem to be a better language than JavaScript in some ways.  Too bad it didn't come first.

      Here is a very terse summary of Dart features (my raw notes from some recent reading), to give you an idea of what it's all about.  It tries to combine the best of JavaScript, Java, Ruby, Python, Groovy, etc.

      - Type annotations are for compile-time tools.  Ignored at runtime.
        - var vs String, int, etc.
      - --checked mode
      - Has tools:
        - Dart Editor
        - Dartium -- a custom build of Chromium with the Dart VM embedded.
        - Eclipse plugin:  http://www.dartlang.org/eclipse/update.  
        - dart_analyzer -- static analyzer
        - Dart2js -- compiles Dart source code to JavaScript
          - Does "treeshaking" to remove dead code as it goes
        - Dartdoc - javadocs
        - pub - package manager, pulls in library dependencies from the web.
          - pubspec.yaml -- config info for a Dart project
      - Folder tree:
          project_root/
            pubspec.yaml
            README.md
            LICENSE
            lib/
              awesome.dart
              src/
                functions.dart
                classes.dart
            web/
              index.html
              index.css
              index.dart
            test/
              awesome_test.dart
            pubspec.lock (version numbers of dependencies)
            /packages/
            doc/
            bin/
            example/
            tool/
      - Sample pubspec.yaml
          name: my_awesome_library
          version: 1.2.3
          description: 
            Increases the awesomeness of your Dart code by a factor
            of seven.  
          author: you@your.email.com
          homepage: http://your.library.com/
          dependencies: 
            unittest: any
            logging: 1.2.3
            http: '>=1.0 <2.5'
            mylib: 
              git: git://github.com/you/myLib.git
      - pub commands:
        % pub install
        % pub update
        % pub publish
      - Object:
        - hashCode()
        - toString()
      - Primitives:  num, int, double, bool
      - Only true is true.  Everything else is false
      - "is", "is!" operators for RTTI
      - generics:  var myList = new List<String>()
      - For loops
        - for (var i=0;i<10;i++)
        - for (var i in ...)
        - forEach ( (item) { closure } )
      - List, Map, HashSet, Queue, Set
      - Functions:
        - 1st class functions
        - Functions nested in functions
        - Methods of classes
      - Optional params, which may be named params
      - Function pointers
      - Typedefs for enforcing signatures of function pointers as params
      - Closures
      - Classes and Interfaces
          class Language {
            String name; // public field
            int _version; // private field: _ prefix denotes private
            static url = "http://www.dartlang.org"; // same value 
                                                    // exists across
                                                    // all instances 
            final awesomeness = 11;  // immutable after 
                                     // constructor initialization
          
            // default constructor  
            Dart() {
              name = "Dart";
              _version = 1;
            }
          
            // named constructor with initializer to initialize 
            // final variables before class construction.
            Dart.withExtraAwe(aweValue) : awesomeness=aweValue {
              name = "Dart";
              _verison = 1; 
            }
            
            // Getter and setter pair.  Getters and setters are 
            // independent of each other, so you can have one 
            // without the other  
            int get version => _version; // shorthand syntax 
                                         // returns _version
            set version(int val) {  // longhand syntax
               _version = val; // sets the _version
            }
          
            // private method, shorthand syntax
            bool _isAwesome() => this.awesomeness > 10;
          
           
            // public method
            void checkAwesomeness() {
              if (_isAwesome) {
                print("$name $version is $awesomeness out of 10"); 
                // prints: "Dart 1 is 11 out of 10"
              }
            }
             
          }
      - Property syntax for accessing getters/setters
      - Java/C# type class syntax
        - extends
        - super() 
        - abstract class
        - implements
      - Libraries
        - Instead of packages
        - Leading underscore makes a member private to library, not to class
          

      CoffeeScript is a similar effort but less radical a change from JavaScript.  Its syntax is a step in the direction of Python, with indentation determining nesting levels.  I'm not sure if that's good or bad, but I'm doing a lot of Python programming lately, and I do like the conciseness and readability.  CoffeeScript compiles to JavaScript and catches lots of simple JavaScript programmer mistakes along the way.

      TypeScript is another similar effort, but even less of a change from JavaScript.  Like CoffeeScript, it adds type checking to JavaScript, but it does so via minor extensions to the existing JavaScript syntax.  Like Dart and CoffeeScript, it compiles into JavaScript.  However, it is a product of Microsoft which makes me leery.  From my experience, they have a bad track record on quality and security (Windows, Office, etc.), they don't even try to keep new releases backwards compatible with old releases (VB7), they often abandon products even though people are relying heavily on them (J++), and their ethics are the worst in the industry.  So, personally, I'd steer clear.

      On the other hand, Microsoft has open sourced TypeScript, so maybe there's hope for it after all.  With all eyes watching, they won't be able to hide anything malicious in it, and perhaps the outside influence will lead to reasonable quality, security, compatibility, and a useful stable feature set.  If not, the open source community can always fork the product and proceed, as has happened recently with other open source products mis-managed by large proprietary companies: OpenOffice --> LibreOffice, Hudson --> Jenkins, MySQL --> MariaDB, etc.

      Thanks to Kris Molendyke for introducing me to CoffeeScript a couple years ago, where we used it on a project together.

      Thanks to Sonny To for pointing out TypeScript.

      --Fred

    9. See Also

      Last Updated: 3/31/2007
      Applies to:  JavaScript 1.0+

      The following are good sources of info about JavaScript:

      1. Fred Stluka's JavaScript links
      2. Fred Stluka's Ajax links
      3. Any of the O'Reilly books on JavaScript, Ajax, or DHTML.

      --Fred

    ©Copyright 2007-2021, Bristle Software, Inc.  All rights reserved.