The Shell renders and controls the feature containers. These are “top level” contain- ers—usually DIVs—that hold feature content. The Shell initializes and coordinates all the feature modules in the application. And the Shell directs feature modules to cre- ate and manage all content inside feature containers. We’ll discuss feature modules further in chapter 4.
In this section, we’ll first write a method to extend and retract the chat slider fea- ture container. We’ll then build the click event handler so the slider may be opened or closed whenever the user wishes. Then we’ll check our work and talk about the next big thing—managing the page state using the URI hash fragment.
3.5.1 Write a method to extend or retract the chat slider
We’ll be moderately ambitious with our chat slider function. We need it production- quality, but it doesn’t have to be extravagant. Here are the requirements we want to achieve:
Figure 3.6 It’s like deja vu all over again—spa/spa.html
79 Manage the feature containers
1 Enable developers to configure the speed and height of slider motions.
2 Create a single method to extend or retract the chat slider.
3 Avoid a race condition where the slider may be extending and retracting at the same time.
4 Enable developers to pass in an optional callback to be invoked on completion of a slider motion.
5 Create test code to ensure the slider is functioning properly.
Let’s adjust the Shell to meet these requirements as shown in listing 3.11.1 All changes are shown in bold. Please review the annotations as they detail how the changes per- tain to the requirements:
...
spa.shell = (function () {
//--- BEGIN MODULE SCOPE VARIABLES --- var
configMap = {
main_html : String() ...
chat_extend_time : 1000, chat_retract_time : 300, chat_extend_height : 450, chat_retract_height : 15 },
stateMap = { $container : null }, jqueryMap = {},
setJqueryMap, toggleChat, initModule;
//--- END MODULE SCOPE VARIABLES --- //--- BEGIN UTILITY METHODS --- //--- END UTILITY METHODS --- //--- BEGIN DOM METHODS --- // Begin DOM method /setJqueryMap/
setJqueryMap = function () {
var $container = stateMap.$container;
jqueryMap = {
$container : $container,
$chat : $container.find( '.spa-shell-chat' ) };
};
// End DOM method /setJqueryMap/
// Begin DOM method /toggleChat/
// Purpose : Extends or retracts chat slider // Arguments :
// * do_extend - if true, extends slider; if false retracts
1 Now would be a good time to thank your favorite celestial bodies for jQuery, as this would be a lot harder without it.
Listing 3.11 The Shell, revised to extend and retract the chat slider—spa/js/spa.shell.js
Store the retract and extend times and heights in the module configuration map per
Requirement 1: “Enable developers to configure the speed and height of slider motions.”
Add the toggleChat method to our list of module-scope variables.
Cache the chat slider jQuery collection in jqueryMap. Add the
toggleChat method per Requirement 2: “Create a single method to extend or retract the chat slider.”
// * callback - optional function to execute at end of animation // Settings :
// * chat_extend_time, chat_retract_time // * chat_extend_height, chat_retract_height // Returns : boolean
// * true - slider animation activated // * false - slider animation not activated //
toggleChat = function ( do_extend, callback ) { var
px_chat_ht = jqueryMap.$chat.height(),
is_open = px_chat_ht === configMap.chat_extend_height, is_closed = px_chat_ht === configMap.chat_retract_height, is_sliding = ! is_open && ! is_closed;
// avoid race condition
if ( is_sliding ){ return false; } // Begin extend chat slider if ( do_extend ) {
jqueryMap.$chat.animate(
{ height : configMap.chat_extend_height }, configMap.chat_extend_time,
function () {
if ( callback ){ callback( jqueryMap.$chat ); } }
);
return true;
}
// End extend chat slider // Begin retract chat slider jqueryMap.$chat.animate(
{ height : configMap.chat_retract_height }, configMap.chat_retract_time,
function () {
if ( callback ){ callback( jqueryMap.$chat ); } }
);
return true;
// End retract chat slider };
// End DOM method /toggleChat/
//--- END DOM METHODS --- //--- BEGIN EVENT HANDLERS --- //--- END EVENT HANDLERS --- //--- BEGIN PUBLIC METHODS --- // Begin Public method /initModule/
initModule = function ( $container ){
// load HTML and map jQuery collections stateMap.$container = $container;
$container.html( configMap.main_html );
setJqueryMap();
// test toggle
setTimeout( function () {toggleChat( true ); }, 3000 );
Prevent a race condition by declining to take action if the slider is already in motion, per Requirement 3:
“Avoid a race condition where the slider may be extending and retracting at the same time.”
Invoke a callback after the animation is complete, per Requirement 4:
“Enable developers to pass in an optional callback to be invoked on completion of a slider motion.”
Extend the slider 3 seconds after page load, and retract it after 8 seconds, per Requirement 5: “Create test code to ensure the slider is functioning properly.”
81 Manage the feature containers
setTimeout( function () {toggleChat( false );}, 8000 );
};
// End PUBLIC method /initModule/
return { initModule : initModule };
//--- END PUBLIC METHODS --- }());
If you are playing along at home, let’s first check our code with JSLint by typing jslint spa/js/spa.shell.js in the command line—we shouldn’t see any warnings or errors. Next let’s reload the browser document (spa/spa.html) and see the chat slider extend after three seconds and retract after eight seconds. Now that we have the slider moving, we can employ a user’s mouse-click to toggle its position.
3.5.2 Add the chat slider click event handler
Most users expect to click on a chat slider and see it extend or retract, as this is the common convention. Here are the requirements we want to achieve:
1 Set tool-tip text to prompt user action, for example “Click to retract.”
2 Add a click event handler to call toggleChat.
3 Bind the click event handler to the jQuery event.
Let’s adjust the Shell to meet these requirements as shown in listing 3.12. All changes are again shown in bold, and the annotations detail how the changes pertain to the requirements.
...
spa.shell = (function () {
//--- BEGIN MODULE SCOPE VARIABLES --- var
configMap = { ...
chat_retract_height : 15,
chat_extended_title : 'Click to retract', chat_retracted_title : 'Click to extend' },
stateMap = {
$container : null, is_chat_retracted : true },
jqueryMap = {},
setJqueryMap, toggleChat, onClickChat, initModule;
//--- END MODULE SCOPE VARIABLES --- ...
//--- BEGIN DOM METHODS --- // Begin DOM method /setJqueryMap/
...
// End DOM method /setJqueryMap/
Listing 3.12 The Shell, revised to handle chat slider click events—spa/js/spa.shell.js
Add retracted and extended title text to the configMap per Requirement 1:
“Set tool-tip text to prompt user action...”
Add is_chat_retracted to the stateMap. It’s good practice to list all keys used in the stateMap so they can be easily found and inspected. This is used by our toggleChat method.
Add onClickChat to our list of module-scope function names.
// Begin DOM method /toggleChat/
// Purpose : Extends or retracts chat slider ...
// State : sets stateMap.is_chat_retracted // * true - slider is retracted
// * false - slider is extended //
toggleChat = function ( do_extend, callback) { var
px_chat_ht = jqueryMap.$chat.height(),
is_open = px_chat_ht === configMap.chat_extend_height, is_closed = px_chat_ht === configMap.chat_retract_height, is_sliding = ! is_open && ! is_closed;
// avoid race condition
if ( is_sliding ) { return false; } // Begin extend chat slider
if ( do_extend ) {
jqueryMap.$chat.animate(
{ height : configMap.chat_extend_height }, configMap.chat_extend_time,
function () {
jqueryMap.$chat.attr(
'title', configMap.chat_extended_title );
stateMap.is_chat_retracted = false;
if ( callback ) { callback( jqueryMap.$chat ); } }
);
return true;
}
// End extend chat slider // Begin retract chat slider jqueryMap.$chat.animate(
{ height : configMap.chat_retract_height }, configMap.chat_retract_time,
function () {
jqueryMap.$chat.attr(
'title', configMap.chat_retracted_title );
stateMap.is_chat_retracted = true;
if ( callback ) { callback( jqueryMap.$chat ); } }
);
return true;
// End retract chat slider };
// End DOM method /toggleChat/
//--- END DOM METHODS --- //--- BEGIN EVENT HANDLERS ---
onClickChat = function ( event ) {
toggleChat( stateMap.is_chat_retracted );
return false;
};
Update the toggleChat API docs to indicate how stateMap.is _chat_retracted is set by this method.
Adjust toggleChat to control the hover text as well as the stateMap .is_chat_retracted value per Requirement 1: “Set tool-tip text to prompt user action...”
Add the onClickChat event handler per Requirement 2: “Add a click event handler to call toggleChat.”
83 Manage the feature containers
//--- END EVENT HANDLERS --- //--- BEGIN PUBLIC METHODS --- // Begin Public method /initModule/
initModule = function ( $container ) { // load HTML and map jQuery collections stateMap.$container = $container;
$container.html( configMap.main_html );
setJqueryMap();
// initialize chat slider and bind click handler stateMap.is_chat_retracted = true;
jqueryMap.$chat
.attr( 'title', configMap.chat_retracted_title ) .click( onClickChat );
};
// End PUBLIC method /initModule/
return { initModule : initModule };
//--- END PUBLIC METHODS --- }());
Those playing along at home should again check our code by typing jslintspa/js/
spa.shell.js at the command line. We again shouldn’t see any warnings or errors.
There’s an aspect of jQuery event handlers that we think is crucial to remember: the return value is interpreted by jQuery to specify its continued handling of the event. We usually return false from our jQuery event handlers. Here’s what that does:
■ It tells jQuery to prevent the default action—like following a link, or selecting text—from occurring. The same effect can be acquired by invoking event.pre- ventDefault() in the event handler.
■ It tells jQuery to stop the event from triggering the same event on the parent DOM element (this behavior is often called bubbling). The same effect can be acquired by invoking event.stopPropagation() in the event handler.
■ It concludes the handler execution. If the clicked element has other handlers bound to it after this handler, the next one in line will be executed. (If we don’t want subsequent handlers to execute, we can invoke event.preventImmedi- atePropagation().)
These three actions are usually what we want our event handlers to do. Soon we’ll write event handlers where we do not want these actions. These event handlers will return the true value.
The Shell doesn’t need to necessarily handle the click. It could instead provide the capability to manipulate the slider as a callback to the chat module—and we encourage this. But, because we haven’t written that module yet, we have handled the click event in the Shell for now.
Now let’s add a little flair to our Shell styles. Listing 3.13 shows the changes:
Initialize the event handler by setting stateMap.is_chat _retracted and the hover text. Then bind the handler to a click event per Requirement 3: “Bind the click event handler to the jQuery event.”
...
.spa-shell-foot { ...
}
.spa-shell-chat {
bottom : 0;
right : 0;
width : 300px;
height : 15px;
cursor : pointer;
background : red;
border-radius : 5px 0 0 0;
z-index : 1;
}
.spa-shell-chat:hover { background : #a00;
}
.spa-shell-modal { ... } ...
When we reload the browser document (spa/spa.html) we can click on the slider and see it extend as shown in figure 3.7:
The slider extends much more slowly than it retracts. We can change the speed of the slider by changing the configuration in the Shell (spa/js/spa.shell.js), for example:
...
configMap = {
main_html : String() ...
chat_extend_time : 250, chat_retract_time : 300, ...
}, ...
In the next section, we’ll adjust our application to better manage its state. When we’re finished, all browser history features like Bookmarks, the Forward button, and the Back button will work for the chat slider as the user expects.
Listing 3.13 Adding some flair to the Shell—spa/css/spa.shell.css
Change the cursor to a pointer when hovering over the slider. This informs the user that something will happen if they click.
Round a corner to make the slider look nicer.
Change the slider color when the cursor hovers over the slider. This reinforces the message to the user that an action is available on click.
(1) Click here (2) Slides out
Figure 3.7 Extending the chat slider—spa/spa.html
85 Manage application state