Chapter 10. Navigation Boxes and Windows

Alert boxes are used in online applications to a far greater extent today than they were even five years ago. Providing messages to the user, errors and warnings, and even small application notifications, alert boxes have become a part of everyday life on the Web. Along with the alert box, as well as its siblings (the prompt box and confirmation box), navigation boxes are also becoming the norm. These boxes are part of the application, not the client. This distinction allows the navigation box to fit in more seamlessly than the client’s alert boxes.

These boxes and windows can have far-ranging functionality within a web application. Therefore, we will take a closer look not only at how to create these boxes, but also at how to use them effectively with Ajax driving the content.

The Alert Box

The alert box takes many forms depending on the theme of the desktop and the browser being used. This makes an alert from Internet Explorer look different depending on whether the user is using the default theme, the Windows classic theme, the Windows XP theme, and so forth. This problem will occur across platforms; the alert for Firefox on the Windows platform will look different from the alert for Firefox on the Linux platform. Figure 10-1, Figure 10-2, and Figure 10-3 show different alert windows on Windows, Mac OS X, and Linux platforms, respectively.

Examples of different alert windows on a Windows platform

Figure 10-1. Examples of different alert windows on a Windows platform

Examples of different alert windows on a Mac OS X platform

Figure 10-2. Examples of different alert windows on a Mac OS X platform

Examples of different alert windows on a Linux platform

Figure 10-3. Examples of different alert windows on a Linux platform

The problem with these windows looking so different is that there is never any continuity between the alert window and the application that is being used. We want to change this.

Integrating the Window

To integrate an alert window into a web application, unfortunately you must create the window from scratch. There is no way to visually control the browser components; furthermore, the alert and other boxes are part of the browser and not the page. Because of this, developers must be creative when they want to integrate such boxes into their applications. The easiest way to do this is with the help of a <div> element.

The Window Style

The first part of creating a seamless window system is to build what will be your generic alert box. From this box, you can create all other boxes with whatever content you need inside them. You can use the following simple structure to build such boxes:

<div id="popupContainer">
    <div id="popupHandle">
        <div id="closeThis">
            <img id="closeThisImage" src="close.png" alt="Close Window"
                title="Close Window" />
        </div>
        <div id="handleText">Handle Text</div>
    </div>
    <div id="popupContentWrapper">
        <form id="popupForm" action="" method="post">
            <div id="popupContent">
                This is the Pop-up Content!
            </div>
            <div id="popupButtons">
                <input id="btnOk" type="button" value="OK" />
            </div>
        </form>
    </div>
</div>

The box we are building here closely resembles boxes people are used to seeing in a windowed environment. As you’ll recall from the theme of the preceding chapter, the CSS rules that are used to style this box will closely resemble those that are in the rest of the application. For example:

#popupContainer {
    background-color: #226;
    border-color: #000;
    border-style: solid;
    border-width: 1px;
    color: #fff;
    margin: 0;
    padding: 0;
    width: 460px;
}

#popupHandle {
    background-color: transparent;
    color: #fff;
    padding: 3px 3px 1px 0;
}

#closeThisImage {
    border: none;
    float: right;
    padding: 0;
}

#handleText {
    background-color: transparent;
    color: fff;
    font-family: "Trebuchet MS", Arial, sans-serif;
    font-size: 1.2em;
    font-weight: bold;
    padding-left: 6px;
}

#popupContentWrapper {
    background-color: #ddf;
    border-color: #000;
    border-style: solid;
    border-width: 1px;
    color: #000;
    margin: 1px;
}

#popupContent {
    font-family: Tahoma, serif;
    font-size: 1em;
    height: 200px;
    overflow: hidden;
    padding: 15px;
    text-align: left;
}

#popupButtons {
    padding: 10px 0;
    text-align: center;
}

#popupButtons input[type="button"] {
    padding: 2px 20px;
}

This CSS will give us a pop-up box that looks something like Figure 10-4.

A generic pop-up box seamlessly integrated into the application

Figure 10-4. A generic pop-up box seamlessly integrated into the application

The structure of the pop-up box will allow flexibility when it comes to how the pop-up window will look. This is exactly what we want—something that is easy to integrate and flexible enough in structural design to fit almost any application’s CSS rules.

Of course, having such a pop-up box is completely useless unless it functions like a normal alert window. What is primarily lacking in our window is the ability to drag it around the application, close it, and have it accept a user-supplied value. We will leave accepting a value for a little later, as that will involve some Ajax scripting. One thing we cannot forget is that most pop-up windows steal focus from an application until they are closed. We can accomplish that with just a little JavaScript.

Because keeping focus on the pop up and closing the pop up are closely related, we will focus on both of them at the same time. The easiest part is closing the pop-up window. Consider the following XHTML code:

<div id="popupContainer">
    <div id="popupHandle">
        <div id="closeThis" onclick="closePopUp( );">
            <img id="closeThisImage" src="close.png" alt="Close Window"
                title="Close Window" />
        </div>

As you can see, an onclick event is added to our closing X image that will hide our pop-up box until it is needed again. The first thing we need for our new integrated pop-up window is a way to open it. The openPopUp( ) function shows a predetermined <div> element called popupContainer and sets the focus of the page to the pop-up window’s OK button:

/**
 * This function, openPopUp, "opens" up our custom pop-up window and sets the
 * focus of the page to the pop-up window's /OK/ button.
 */
function openPopUp( ) {
    /*
     * This function is using the Prototype Element.show( ) method instead of just
     * simply doing this:
     *     $('popupContainer').style.display = 'block'; - because we will be using
     *         Prototype (actually script.aculo.us) in a little bit to make the
     *         pop-up window movable anyway.
     */
    Element.show('popupContainer'),
    $('btnOk').focus( );
}

The closePopUp( ) function is pretty simple to implement, because all it needs to do is hide the pop-up window:

/**
 * This function, closePopUp, "closes" down our custom pop-up window.
 */
function closePopUp( ) {
    /*
     * This function is using the Prototype Element.hide( ) method instead of just
     * simply doing this:
     *     $('popupContainer').style.display = 'none'; - because we will be using
     *         Prototype (actually script.aculo.us) in a little bit to make the
     *         pop-up window movable anyway.
     */
    Element.hide('popupContainer'),
}

Keeping focus on the pop-up window is a little more involved. We will need an event that will check to see whether the user tries to change focus from the pop up; when the focus changes, we want the focus to go right back to the pop-up window. We use the focusOnPopUP( ) function to handle this by monitoring the user’s mouse clicks and determining whether the click was on the opened pop-up window or somewhere else on the page. We then must set the focus of the page to the correct element. Example 10-1 accomplishes this.

Example 10-1. A function to keep a page’s focus on a custom pop-up window

/**
 * This function, focusOnPopUp, traps all mouse clicks on the page, and when our
 *custom pop-up window is "open", it determines if the click was on the pop-up
 * window or elsewhere on the page.  When the mouse click is not on the pop-up
 * window, the event is stopped and the focus is returned to the pop-up window.
 * Otherwise, the focus goes where it was intended and the event carries on as
 * usual.
 *
 * @param {Event} e The event that has fired in the browser.
 */
function focusOnPopUp(e) {
    /*
     * This is the cross-browser way of getting the target of the event in
     * question.
     */
    var el = ((e.target) ? e.target : e.srcElement);

    /* Is our pop-up window currently active? */
    if (Element.visible('popupContainer')) {
        /* Is the event target our pop-up window? */
        if (el.id != 'popupContainer') {
            var childNode = false;

            /*
             * Walk the DOM and find out if this element is a child of the
             * /popupContainer/
             */
            for (var child = el.parentNode; child.tagName != 'BODY';
                    child = child.parentNode)
                /* Is the target element a childNode of the pop-up container? */
                if (child.id == 'popupContainer') {
                    childNode = true;
                    break;
                }
            /* Is the event part of our pop-up window? */
            if (!childNode) {
                Event.stop(e);
                $('btnOk').focus( );
            /* Give the event target focus, since it is part of the pop-up window */
            } else
                el.focus( );
        }
    /* Give the event target focus, since the pop-up window is not active */
    } else
        el.focus( );
}

Then we need to set an event listener for which this function will act:

Event.observe(document, 'click', focusOnPopUp, true);

We listen for onclick events at the document level because we need to parse through every click that occurs on the page and decide where the event happened. Unfortunately for us, the World Wide Web Consortium (W3C) Document Object Model (DOM) Recommendation does not have onfocus and onblur events associated with <div> elements. Therefore, we are forced to listen to clicks as they occur.

Because our event listener is at the document level, every click will pass through it. You also may have noticed that the last parameter passed to the Event.observe( ) method was set to true instead of the false that we normally pass to it. We do this so that we don’t have to worry about other events bubbling up after this event fires if events need to be stopped. The first thing our function checks for is whether the pop-up window is even visible to the user; if the pop up does not technically “exist” to the user, there is no need to go any further. Once this check is made, the function must determine whether focus is still somewhere in the pop-up window or elsewhere so that focus is placed on the correct element. We accomplish this by walking the DOM backward until we hit either our popupContainer element or the <body> element.

Now our pop-up window is beginning to function like users would expect it to. The functionality that our pop up still lacks, however, is the ability to drag the box anywhere in the application. It’s easiest to do this using one of the JavaScript libraries.

Moving the Window

script.aculo.us is a very easy JavaScript library to use when you need dragging functionality. The following JavaScript, executed once the page is loaded, is all you need to make an element draggable in the browser:

new Draggable('popupContainer', {
    handle: 'popupHandle',
    zindex: 99999,
    starteffect: false,
    endeffect: false
})

You can pass several options in the object parameter, as shown in Table 10-1.

Table 10-1. The available options for the script.aculo.us Draggable object

Options

Description

Default

constraint

This option sets whether the element will be constrained when it is dragged around the screen. Possible values are none, 'horizontal', and 'vertical', the latter two of which constrain the element to the horizontal or vertical direction.

None

endeffect

This option sets the effect that will be used when the draggable element stops being dragged. Possible values for this option are false and an effect such as 'Opacity'.

'Opacity'

ghosting

This option sets whether the draggable element should be cloned and the clone should actually be dragged, leaving the original element in place until the clone is dropped. Possible values are true and false.

false

handle

This option sets whether the draggable element will be dragged by an embedded handle or by the whole element. The option should be an element reference, an element id, or a string referencing a CSS class value. For the className, the first child element, first grandchild element, and so on found within the draggable element with a matching className will be used as the handle.

None

revert

This option sets what should happen when the draggable element is dropped. When this option is set to true, the element returns to its original position when the drag ends. When this option is set to a function reference, the named function will be called when the drag ends.

false

reverteffect

This option sets the effect that will be used when the element reverts to its original position based on the value of the revert option. This option is ignored if the revert option is set to false; otherwise, it can be any effect, such as 'Move'.

'Move'

snap

This option determines whether the draggable element should be snapped to a defined grid. When this option is set to false no snapping occurs; otherwise, the option takes one of the following forms:

  • xy

  • [x, y]

  • function(x, y) { return [x, y]; }

false

starteffect

This option sets the effect that will be used when the draggable element starts being dragged. Possible values for this option are false or an effect such as 'Opacity'.

'Opacity'

zindex

This option sets the CSS z-index property of the draggable element.

1000

Executing the following JavaScript function once the page has loaded will successfully enable our pop-up window to be draggable within the application.

Warning

There is one significant problem with creating a custom draggable pop-up window within an application. Until Internet Explorer 7, certain elements (windowed elements) on the page would not respect the index property that was set on them. Examples of this are <select> and <object> elements. The problem with the <select> element, for example, is that it is rendered using a Windows object instead of an XHTML object, disregarding any z-index properties set.

When a draggable box is moved on top of this kind of rendered element, the element in question will remain displayed on top of the draggable box regardless of how its z-index is set, as shown in Figure 10-5.

Until Internet Explorer 7 wipes out use of any earlier version of IE, the rendering bug in Figure 10-5 will remain a problem. Fortunately, a simple hack can resolve it.

The simplest way to stop this rendering issue is to make it go away. When our pop-up window is activated, all instances of elements with rendering issues need to be hidden on the page. This way, nothing that the navigation window is dragged over will be rendered incorrectly.

An Internet Explorer rendering bug

Figure 10-5. An Internet Explorer rendering bug

Here is some simple code you can use to toggle the visibility of windowed elements:

/**
 * This function, hideElements, hides all of the windowed elements (<select>,
 * <object>) in the document from the user so that there is no problem with z-index
 * order on the page while the pop up is "open".
*/
function hideElements( ) {
    /* Get a list of all of the /select/ elements */
    var selects = document.getElementsByTagName('select'),

    /* Loop through the elements and hide all of them */
    for (var i = selects.length; i > 0; i--)
        selects[(i - 1)].style.visibility = 'hidden';

    /* Get a list of all of the /object/ elements */
    var objects = document.getElementsbyTagName('object'),

    /* Loop through the elements and hide all of them */
    for (var i = objects.length; i > 0; i--)
        objects[(i - 1)].style.visibility = 'hidden';
}

/*
 * This function, showElements, shows all of the windowed elements (<select>,
 * <object>) in the document to the user again once the pop-up window is "closed".
 */
function showElements( ) {
    /* Get a list of all of the /select/ elements */
    var selects = document.getElementsByTagName('select'),

    /* Loop through the elements and show all of them */
    for (var i = selects.length; i > 0; i--)
        selects[(i - 1)].style.visibility = 'visible';

/* Get a list of all of the /object/ elements */
    var objects = document.getElementsbyTagName('object'),

    /* Loop through the elements and show all of them */
    for (var i = objects.length; i > 0; i--)
        objects[(i - 1)].style.visibility = 'visible';
}

This is an ugly hack, I admit, but until Internet Explorer 7 replaces Internet Explorer 6 as the dominant browser, all developers will have to live with it. With the addition of this code, our pop-up window should now render and behave like the client’s windows and boxes do. Internet Explorer is the only browser that has these rendering issues; therefore, a simple browser check will take care of when the necessary code needs to be called, as there is no reason to add more burden to a browser that already renders elements correctly.

Tip

There is another solution to our rendering bug issue in Internet Explorer. This involves placing an <iframe> element directly behind our pop-up window. Even though Internet Explorer does not honor the z-index property in most cases, it actually does in the case of the <iframe> element. Part of Mike Hall’s BrainJar “Revenge of the Menu Bar” tutorial discusses this issue, and I’ll defer to it since it requires using a Frameset DOCTYPE to remain compliant; see http://www.brainjar.com/dhtml/menubar/default11.asp for details.

Navigation Windows

By integrating any custom pop-up window into the Ajax application, you are essentially creating a window that aids in the application’s functionality or navigation. From this point on, when I refer to a navigation window, I am talking about the pop-up window that was developed to integrate with the application as opposed to the browser’s alert box and its siblings. You can use navigation windows for more than just alerting the user to some text, or prompting the user for an OK or cancel command. We can use our custom navigation window to present an associating form to the user, or additional information as requested by her.

By using our custom navigation window, we have full reign over what roles the user wants the navigation window to take. Besides replacing the browser’s alert, prompt, and confirm windows, the developer can present any kind of data to the user in a window that is certain to require her attention.

Placing Content into Windows

You can place in the innerHTML property of the window’s <div> element any content that the application needs to add to a navigation window. For example:

$('popupContainer').innerHTML = 'This is the new content to be displayed in
the navigation window.';

This makes it easy for a developer to insert content that has been passed from the server using an Ajax request.

Whether the developer wants to use the responseText property or the responseXML property of the Ajax response is a matter of choice. I already showed you how to import XML using the custom _importNode( ) method from Example 9-3 in Chapter 9. An even easier method is to put the responseText from the Ajax response into the innerHTML of the navigation window.

Warning

Just as there is sometimes an issue with an event attribute not firing when the element it is associated with was imported from an external DOM document, some browsers (particularly Internet Explorer) still do not initially recognize attribute events for what they are. Therefore, it is sometimes wise to detect the browser, and when the browser will be an issue, to set the innerHTML property of the navigation window equal to itself. For example:

$('popupContainer').innerHTML = $('popupContainer').
innerHTML;

Information Boxes

Information boxes are the easiest boxes to implement, because all they require is some text and a button to close the window. The standard case for the information box is the alert—whether that alert is to allow the user to see when an error has occurred, or just to get the user’s attention before moving on with the application. Figure 10-6 shows a common example of an information box.

One example of an information box

Figure 10-6. One example of an information box

We need a way to pass to our information window the data (text or otherwise) that should be passed to the user. Example 10-2 shows what such a function looks like.

Example 10-2. A function to pass data to an information window

/**
 * This function, fillPopUp, takes the data that is passed to it in the /p_data/
 * parameter and sets it equal to the innerHTML of the pop up's content container,
 * and the data that is passed in the /p_header/ parameter and sets it equal to the
 * innerHTML of the pop up's handle text.  It then "opens" the pop-up window.
 *
 * @param {String} p_data The string containing the data to set to the pop-up
 *     element's /innerHTML/.
 * @param {String} p_header The string containing the pop-up element's "header".
 */
function fillPopUp(p_data, p_header) {
    if (p_header)
        $('handleText').innerHTML = p_header;
    else
        $('handleText').innerHTML = 'Alert window';
    $('popupContent').innerHTML = p_data;
    /*
     * This is for Internet Explorer in case the p_data passed in contained event
     * attributes, just to make sure that they fire correctly for the user.
     */
    $('popupContent').innerHTML = $('popupContent').innerHTML;
    /* "Open" up the pop-up window for the user to see. */
    showPopUp( );
}

It’s more important for an application’s custom navigation window to fully replace the functionality of the browser’s prompts and confirmation pop-up boxes. Just as with the default browser’s boxes, the user’s response can quickly be passed to the server with an Ajax call as normal functionality resumes in the application.

Replacing Alerts, Prompts, Confirms, and So On

All alerts, prompts, confirms, and so on that are presented to the user require the user to interact with the window in some way. In most cases, this interaction is in the form of selecting one button from possible choices, with that choice being used by the application. Figure 10-7 shows an example of this kind of navigation window.

A typical confirmation window

Figure 10-7. A typical confirmation window

The confirmation box in Figure 10-7 expects the user to click either the OK button or the Cancel button at the bottom of the window. Example 10-3 shows how you could implement this box to accept the user’s click and pass it on to an Ajax request to the server.

Example 10-3. The code for a functional confirmation window

/**
 * This function, onConfirmOkay, is the handler for an onclick event on the OK
 * button of a confirmation window.  It makes an XMLHttpRequest, passing to the
 * server some predetermined data, reporting back to the user if there is a
 * failure; otherwise, it provides some other functionality.
 *
 * @return Returns false, so the form is not actually submitted.
 * @type Boolean
 */
function onConfirmOkay( ) {
    /*
     * Make sure that the confirmation data that we wish to send to the server is
     * actually there.
     */
    if (!$F('confirmedData'))
        $('confirmedData').value = '001 - Bad confirmation received from user.';
    new Ajax.Request('saveConfirm.php', {
        method: 'post',
        parameters: 'data=' + $F('confirmData'),
        onSuccess: function(xhrResponse) {
            /* Do something here */
        },
        onFailure: function(xhrResponse) {
            /* Send the error message to the user */
            fillPopUp('There was an internal error with the application: <br />' +
                xhrResponse.statusText);
        }
    });
    /* "Close" the confirmation window after the Ajax request has gone out */
    closeConfirm( );
    return (false);
}

/**
 * This function, onConfirmCancel, is the handler for an /onclick/ event on the
 * Cancel button of a confirmation window.  It closes the confirmation window after
 * clearing out whatever input was to be confirmed.
 *
 * @return Returns false, so the form is not actually submitted.
 * @type Boolean
 */
function onConfirmCancel( ) {
    /* Clear out the input box being confirmed */
    $('confirmedData').value = '';
    /* "Close" the confirmation window so the user can do something else */
    closeConfirm( );
    return (false);
}

/* Open up a confirmation window */
var confirmationQuestion = 'Are you sure you want to leave this page before you ' +
    'have completed all of your answers to this test?';
fillConfirmation(confirmationQuestion, 'Are you sure you want to leave?'),

/**
 * This function, fillConfirmation, takes the data that is passed to it in the
 * /p_data/ parameter and sets it equal to the /innerHTML/ of the confirmation
 * window's content container, and the data that is passed in the /p_header/
 * parameter and sets it equal to the /innerHTML/ of the confirmation window's
 * handle text.  It then "opens" the confirmation window.
 *
 * @param {String} p_data The string containing the data to set to the pop-up
 *     element's /innerHTML/.
 * @param {String} p_header The string containing the pop-up element's "header".
 */
function fillConfirmation(p_data, p_header) {
    if (p_header)
        $('handleConfirmText').innerHTML = p_header;
    else
        $('handleConfirmText').innerHTML = 'Alert window';
    $('confirmContent').innerHTML = p_data;
    /*
     * This is for Internet Explorer in case the p_data passed in contained event
     * attributes, just to make sure that they fire correctly for the user.
     */
    $('confirmContent').innerHTML = $('confirmContent').innerHTML;
    /* "Open" up the confirmation window for the user to see. */
    showConfirm( );
}

The only difference from our first information window is that we need two buttons, and they must be wired to events, like this:

<div id="popupButtons">
    <input id="btnConfirmOk" type="button" value="OK"
        onclick="return onConfirmOk( );" />&nbsp;&nbsp;&nbsp;
    <input id="btnConfirmCancel" type="button" value="Cancel"
        onclick="return onConfirmCancel( );" />
</div>

The prompt window shown in Figure 10-8 leads the way to even more important navigation windows that can be created. Example 10-4 shows the code for a working prompt window.

A typical prompt window

Figure 10-8. A typical prompt window

Example 10-4. The code for a functional prompt window

/**
 * This is the global prompt variable that can be accessed by the application at
 * any time.
 */
var promptInputData = '';

/**
 * This function, onPromptOkay, is the handler for an onclick event on the OK
 * button of a prompt window.  It sets a variable that is accessible to the rest of
 * the application.
 *
 * @return Returns false, so the form is not actually submitted.
 * @type Boolean
 */
function onPromptOkay( ) {
    /* Make sure that the prompt data that we wish to set is actually there */
    if (!$F('promptData')) {
        promptInputData = '';
        /* Set up a new event for the information window */
        try {
            Event.observe($('btnOk'), 'click', firstPrompt( ), false);
        } catch (ex) {}
        fillPrompt('You did not fill anything in the input field.',
            'There was a problem'),
    } else {
        /* Set our global variable to the user's input */
        promptInputData = $F('promptData'),
        /* Clean up after ourselves for the next use */
        $('promptData').value = '';
    }
    /* "Close" the prompt window after the Ajax request has gone out */
    closePrompt( );
    return (false);
}

/**
 * This function, onPromptCancel, is the handler for an /onclick/ event on the
 * Cancel button of a prompt window.  It closes the prompt window after clearing
 * out whatever input was prompted for.
 *
 * @return Returns false, so the form is not actually submitted.
 * @type Boolean
 */
function onPromptCancel( ) {
    /* Clear out the input box being prompted for */
    $('promptData').value = '';
    promptInputData = '';
    /* "Close" the pop-up window so the user can do something else */
    closePopUp( );
    return (false);
}

/**
 * This function, firstPrompt, is the handler for the page load, and for re-
 * prompting the user if there is an issue with any previous prompt.  It calls the
 * prompt window.
 */
function firstPrompt( ) {
    fillPrompt('What should we call you?', 'Name, please...'),
}

/**
 * This function, fillPrompt, takes the data that is passed to it in the /p_data/
 * parameter and sets it equal to the /innerHTML/ of the prompt window's content
 * container, and the data that is passed in the /p_header/ parameter and sets it
 * equal to the /innerHTML/ of the prompt window's handle text.  It then "opens"
 * the prompt window.
 *
 * @param {String} p_data The string containing the data to set to the pop-up
 *     element's /innerHTML/.
 * @param {String} p_header The string containing the pop-up element's "header".
 */
function fillPrompt(p_data, p_header) {
    if (p_header)
        $('handlePromptText').innerHTML = p_header;
    else
        $('handlePromptText').innerHTML = 'Alert window';
    $('promptContent').innerHTML = p_data;
    /*
     * This is for Internet Explorer in case the p_data passed in contained event
     * attributes, just to make sure that they fire correctly for the user.
     */
    $('promptContent').innerHTML = $('promptContent').innerHTML;
    /* "Open" up the prompt window for the user to see. */
    showPrompt( );
}

Again, prompts would require only a small change from confirmation windows in that the attribute events would fire off different functions, like so:

<div id="popupButtons">
    <input id="btnPromptOk" type="button" value="OK"
        onclick="return onPromptOk( );" />&nbsp;&nbsp;&nbsp;
    <input id="btnPromptCancel" type="button" value="Cancel"
        onclick="return onPromptCancel( );" />

    </div>

The other windows I alluded to contain larger forms that can be passed directly to the server from the navigation window. At the same time, the application can also use this information without having to wait for the server to send back a response. Example 10-5 shows how this can work.

Example 10-5. A larger form in action

/**
 * This function, onPromptOkay, is the handler for the /onclick/ event of the OK
 * button of our advanced form pop-up window.  It checks to make sure that all of
 * the required fields are filled out by the user, then makes an XMLHttpRequest to
 * send the information to the server to be saved while it lets the application go
 * on with its normal functions.  The XMLHttpRequest response is parsed only if an
 * error occured in the transaction.
 *
 * @return Returns false, so the form is not actually submitted.
 * @type Boolean
 */
 function onPromptOkay( ) {
    /* Are the required fields in the pop-up set? */
    if (!$F('lastName') || !F('city') || $F('zipCode') || $F('email')) {
        fillPrompt('You did not fill in all of the required input fields.',
            'Fill in all required fields'),
        $('btnOk').focus( );
    } else {
        /* Create the parameter string for the form */
        var params = 'last=' + $F('lastName') +
            (($F('firstName')) ? '&first=' + $F('firstName') : '') +
            (($F('address')) ? '&address=' + $F('address') : '') +
            '&city=' + $F('city') +
            (($F('state')) ? '&state=' + $F('state') : '') +
            '&zip=' + $F('zipCode') +
            (($F('phone')) ? '&phone=' + $F('phone') : '') +
            '&email=' + $F('email'),

        new Ajax.Request('saveConfirm.php', {
            method: 'post',
            parameters: params,
            onFailure: function(xhrResponse) {
                /* Send the error message to the user */
                fillPopup('There was an internal error with the application: <br />'
                    + xhrResponse.statusText);
            }
        });
    }
    /* "Close" the prompt window after the Ajax request has gone out */
    closePrompt( );
    return (false);
}

Figure 10-9 shows how this would look.

A larger form in the navigation window

Figure 10-9. A larger form in the navigation window

Tool Tips

You can think of tool tips as another form of pop-up window depending on their functionality. By functionality, I mean the following:

  • Does the tool tip contain more than just text?

  • Does the tool tip respect the edges of the browser?

  • Is the tool tip customizable? (Unlike using a title attribute and element.)

Example 10-6 shows the code for implementing a customizable tool tip such as that shown in Figure 10-10.

A tool tip in action

Figure 10-10. A tool tip in action

Example 10-6. A customizable tool-tip object

/**
 * @fileoverview The file, tooltip.js, tracks the position of the mouse pointer,
 * and when placed over a designated element that contains more information,
 * creates a tool tip that follows the movement of the mouse as long as the pointer
 * does not leave the designated element.  To make this a more functional tool tip,
 * the code keeps track of the browser's size (even through a window resize) and
 * positions the tool tip around the designated element based on where it will fit
 * within the browser without spilling over.  This makes sure that the tool tip
 * will never be cut off or create unnecessary scroll bars.
 *
 * Elements that are designated as having more information to be shown in a tool
 * tip have a class value of "toolTip" with a complementing element for the tool
 * tip with an id value of the designated element's id value + "Def".  An example
 * would be an element:
 *
 * <span id="tip1" class="toolTip">word</span>
 *
 * with a complementing element for the tool tip:
 *
 * <div id="tip1Def" class="toolTipDef">Word tool-tip</div>
 *
 * The designated element can be any type of element, really, but the element that
 * will be the tool tip must be a block-level element, with the easiest one to
 * choose being the <div> element.
 */

/**
 * This variable keeps track of the mouse position for all tool tips.
 */
var mousePosition = null;
/**
 * This variable keeps track of the window's size for all tool tips.
 */var windowSize = null;

/**
 * This function, mouseMovement, takes an event /e/ as its parameter and sets the
 * global /mousePosition/ variable to an object containing the [x, y] pair
 * representing the position of the mouse pointer when the event happened.
 *
 * @param {Event} e The event that has fired in the browser.
 */
function mouseMovement(e) {
    e = e || window.event;
    /* Is this Internet Explorer? */
    if (e.pageX || e.pageY)
        mousePosition = {
            x: e.pageX,
            y: e.pageY
        };
    else
        mousePosition = {
            x: e.clientX + document.body.scrollLeft - document.body.clientLeft,
            y: e.clientY + document.body.scrollTop - document.body.clientTop
        };
}

/**
 * This function, getResizedWindow, sets the global /windowSize/ variable to an
 * object containing the [x, y] pair representing the width and height of the
 * browser window.
 */
function getResizedWindow( ) {
    windowSize = {
        x: ((document.body.clientWidth) ? document.body.clientWidth :
            window.innerWidth),
        y: ((document.body.clientHeight) ? document.body.clientHeight :
            window.innerHeight)
    };
}

try {
    /* Set up the global events to keep track of mouse movement and window size */
    Event.observe(document, 'mousemove', mouseMovement, false);
    Event.observe(window, 'resize', getResizedWindow, false);
} catch (ex) {}

/**
 * This function, positionToolTip, takes the passed /p_tip/ and calculates where on
 * the page the tool tip should appear on the screen.  This function takes into
 * consideration the outside edges of the browser window to make sure nothing is cut
 * off or causes unnecessary scrolling.  It returns an object with the top and left
 * positions for the tool tip.
 *
 * @param {Node} p_tip The node that is the active tool tip.
 * @return An object containing the top and left positions for the tool tip.
 * @type Object
 */
function positionToolTip(p_tip) {
    /* Calculate the top and left corners of the tool tip */
    var tipTop = mousePosition.y - p_tip.clientHeight - 10;
    var tipLeft = mousePosition.x - (p_tip.clientWidth / 2);

    /* Does the top of the tool tip go outside the window? */
    if (tipTop < 0)
        tipTop = mousePosition.y + 20; /* This is arbitrary, and could be larger */
    /* Does the left of the tool tip go outside the window? */
    if (tipLeft < 0)
        tipLeft = 0;
    /* Does the mouse pointer plus half of the tool tip go beyond the window? */
    if (mousePosition.x + (p_tip.clientWidth / 2) >= windowSize.x - 1)
        tipLeft = windowSize.x - p_tip.clientWidth - 2;
    return ({
        top: tipTop,
        left: tipLeft
    });
}

/**
 * This function, showToolTip, gets the tool tip that is to be shown to the user,
 * positions it according to the mouse position, and sets the CSS styles necessary
 * to make it visible.
 */
function showToolTip( ) {
    /* The tool-tip element to be shown to the user */
    var toolTip = $(this.id + 'Def'),
    /* The position that the tool tip should be in */
    var position = positionToolTip(toolTip);

    Element.setStyle(toolTip, {
        position: 'absolute',
        display: 'block',
        left: position.left,
        top: position.top
    });
}

/**
 * This function, moveToolTip, gets the visible tool tip that is to be moved, and
 * positions it according to the mouse position.
 */
function moveToolTip( ) {
    /* The tool-tip element to be moved with the mouse pointer */
    var toolTip = $(this.id + 'Def'),
    /* The position that the tool tip should be in */
    var position = positionToolTip(toolTip);

    Element.setStyle(toolTip, {
        left: position.left,
        top: position.top
    });
}

/**
 * This function, hideToolTip, gets the visible tool tip that is to be hidden, and
 * hides it.
 */
function hideToolTip( ) {
    /* The tool-tip element to be hidden */
    var toolTip = $(this.id + 'Def'),

    Element.setStyle(toolTip, {
        display: 'none'
    });
}

/**
 * This function, loadToolTips, handles the initial setup of the tool tips by
 * setting event handlers for each of them and getting the initial size of the
 * browser window.  This function is best called on the document.onload event.
 */
function loadToolTips( ) {
    /* A list of elements that are the jumping points for the tool-tip elements */
    var elements = document.getElementsByClassName('toolTip'),

    /* Loop through the list of elements and set event handlers for each of them */
    for (var i = elements.length; i > 0; i--) {
        try {
            Event.observe(elements[(i - 1)], 'mouseover', showToolTip, false);
            Event.observe(elements[(i - 1)], 'mouseout', hideToolTip, false);
            Event.observe(elements[(i - 1)], 'mousemove', moveToolTip, false);
        } catch (ex) {}
    }
    /* Get the initial size of the window */
    getResizedWindow( );
}

Now that we have the code for producing a tool tip, we need to make it a little more functional. Sometimes your tool tip will need to request information from the server before it can display its actual content. With Ajax this is easy. Information from the server could be simple or complex depending on the nature of the tool tip. As we will see in Chapter 17 and Chapter 18, information can be gathered from web services and be displayed to the user in an inline fashion using tool tips. But we are getting ahead of ourselves. For now, we will assume that the server is providing whatever information we require and this information will be displayed to the user in the tool tip. Example 10-7 shows how we can modify our tool-tip object to make an Ajax call and place the server’s response into the content of the tool tip.

Example 10-7. Modifications to the tool-tip object for Ajax functionality

/**
 * This function, showToolTip, gets the tool tip that is to be shown to the user,
 * positions it according to the mouse position, and sets the CSS styles necessary
 * to make it visible.  Get the contents for the tool tip from the server.
 */
function showToolTip( ) {
    /* The tool-tip element to be shown to the user */
    var toolTip = $(this.id + 'Def'),
    /* The position that the tool tip should be in */
    var position = positionToolTip(toolTip);

    /* Has the tool tip already been filled? */
    if ($(this.id + 'Def').innerHTML == '') {
        /* Get the contents for the tool tip from the server */
        Ajax.Request('toolTip.php', {
            method: 'post',
            parameters: 'id=' + toolTip,
            onSuccess: function(xhrResponse) {
                $(this.id + 'Def').innerHTML = xhrResponse.responseText;
            },
            onFailure: function(xhrResponse) {
                $(this.id + 'Def').innerHTML = 'Error: ' + xhrResponse.statusText;
            }
        });
    }

    Element.setStyle(toolTip, {
        position: 'absolute',
        display: 'block',
        left: position.left,
        top: position.top
    });
}

The Necessary Pop Up

Sometimes, creating a draggable <div> element as a customized pop-up window will not work exactly the way you want it to, or it is not a viable option. In this case, your only choice is the standard Windows pop-up boxes and windows. We already discussed the boxes (alert, confirmation, and prompt), but we have not discussed the pop-up window. You create the pop-up window using the open( ) method from the window object. For example:

window.open( );

Of course, this by itself does not really give you much. You can pass some parameters to the open( ) method to give some control regarding the type of window that is opened. The full definition for the open( ) method is:

window.open(url, name, features, replace);

url is an optional string that indicates what URL should be opened in the new window that will be created. name is an optional string that gives the new window a value that can be referenced with the target attribute. features is an optional string of comma-delimited arguments that define the look and functionality of the new window. Table 10-2 lists these features. replace is an optional Boolean that determines whether the new URL loaded into the window should create a new entry in the browser’s history or replace the current history.

Table 10-2. The list of features that can be put in the features string parameter

Feature

Description

height

This feature specifies the height, in pixels, of the new window’s display.

left

This feature specifies the x coordinate, in pixels, of the window.

location

This feature specifies the input field for entering URLs into the window.

menubar

This feature specifies whether the new window will have a menu bar displayed. Values are yes and no.

resizable

This feature specifies whether the new window will be resizable. Values are yes and no.

scrollbars

This feature specifies whether the new window will have horizontal and vertical scroll bars when necessary. Values are yes and no.

status

This feature specifies whether the new window will have a status bar. Values are yes and no.

toolbar

This feature specifies whether the new window will have a toolbar displayed. Values are yes and no.

top

This feature specifies the y coordinate, in pixels, of the window.

width

This feature specifies the width, in pixels, of the new window’s display.

A pop-up window does have some features that make it more desirable to use than a custom window. A pop-up window created with the open( ) method will have all the features a standard window comes with, without you having to go to the trouble of creating them yourself. It’s easy to minimize a pop-up window created with the open( ) method, and the user will still have some notion that it exists, as it will be shown in the operating system’s task bar (extra work is required to get a custom window to be noticeable).

Overall, there is nothing wrong with creating a pop-up window in your application, provided that the user requested it in some way. Pop-up windows become an annoyance and a common complaint when they appear without being requested. Unsolicited pop ups, most often used in Internet advertising, are what have really given pop-up windows a bad name. They are also why all modern browsers now have a pop-up blocker built into their applications. This is important to remember, as the user’s browser may block your pop-up windows unless the user has granted permission to allow them from your application. As such, you should add a note to your application that tells the user that he must not block pop-up windows for it to function properly and fully.

Tip

Ensuring that you have the user’s consent before creating a pop-up window, or any other developer-created window, satisfies the following Web Accessibility Initiative-Web Content Accessibility Guidelines (WAI-WCAG) 1.0 guideline:

  • Priority 2 checkpoint 10.1: Until user agents allow users to turn off spawned windows, do not cause pop ups or other windows to appear and do not change the current window without informing the user.

As far as using Ajax with a pop-up window, the basic principle is the same, whether the new data will be placed in the innerHTML of a <div> element or in the <body> of a pop-up window. Of course, it is just as easy to change the window.location.href of the pop up to the new content whenever it is called for. Either way, pop-up windows are not something that developers should shun when building Ajax applications. Rather, you can view them as useful tools that can enhance the application and maintain its feel within the context of web development.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset