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 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.
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.
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 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.
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.
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
Description | Default | |
---|---|---|
| This option sets
whether the element will be constrained when it is dragged
around the screen. Possible values are none, | None |
| This option sets the
effect that will be used when the draggable element stops
being dragged. Possible values for this option are |
|
| 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
|
|
| 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 | None |
| This option sets what
should happen when the draggable element is dropped. When
this option is set to |
|
| This option sets the
effect that will be used when the element reverts to its
original position based on the value of the |
|
| This option
determines whether the draggable element should be snapped
to a defined grid. When this option is set to
|
|
| This option sets the
effect that will be used when the draggable element starts
being dragged. Possible values for this option are |
|
| This option sets the
CSS |
|
Executing the following JavaScript function once the page has loaded will successfully enable our pop-up window to be draggable within the application.
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.
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.
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.
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.
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.
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 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.
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.
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.
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( );" /> <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.
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( );" /> <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.
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.
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 }); }
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
Description | |
---|---|
| This feature specifies the height, in pixels, of the new window’s display. |
| This feature specifies the x coordinate, in pixels, of the window. |
| This feature specifies the input field for entering URLs into the window. |
| This feature specifies
whether the new window will have a menu bar displayed. Values
are |
| This feature specifies
whether the new window will be resizable. Values are |
| This feature specifies
whether the new window will have horizontal and vertical
scroll bars when necessary. Values are |
| This feature specifies
whether the new window will have a status bar. Values are
|
| This feature specifies
whether the new window will have a toolbar displayed. Values
are |
| This feature specifies the y coordinate, in pixels, of the window. |
| This feature specifies
the |
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.
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.
98.84.25.165