Add frequently needed methods

Một phần của tài liệu single page web applications (Trang 157 - 162)

A few public methods are needed frequently enough in feature modules that they’re worth discussing in their own right. The first is a reset method (removeSlider); the second is a window resize method (handleResize). We’re going to implement both.

First, let’s declare these method names in Chat at the bottom of the Module Scope Variables section, and also export them as public methods at the end of the module, as shown in listing 4.17. Changes are shown in bold:

...

jqueryMap = {},

setJqueryMap, getEmSize, setPxSizes, setSliderPosition, onClickToggle, configModule, initModule,

removeSlider, handleResize

;

//--- END MODULE SCOPE VARIABLES --- ...

// return public methods return {

setSliderPosition : setSliderPosition, configModule : configModule, initModule : initModule, removeSlider : removeSlider, handleResize : handleResize };

//--- END PUBLIC METHODS --- }());

Now with the method names declared, we’ll implement them in the following sec- tions, starting with the remove method.

4.5.1 The removeSlider method

We find that we want a remove method for many of our feature modules. If we imple- ment authentication, for example, we may want to completely remove the chat slider when a user signs out. Usually, this sort of action is taken either to improve perfor- mance or enhance security—assuming the remove method does a good job of deleting obsolete data structures.

Our method will need to delete the DOM container that Chat has appended and otherwise unwind our initialization and configuration, in that order. Listing 4.18 con- tains the code changes for the removeSlider method. Changes are shown in bold:

...

// End public method /initModule/

// Begin public method /removeSlider/

Listing 4.17 Declare method function names—spa/js/spa.chat.js

Listing 4.18 removeSlider method—spa/js/spa.chat.js

133 Add frequently needed methods

// Purpose :

// * Removes chatSlider DOM element // * Reverts to initial state

// * Removes pointers to callbacks and other data // Arguments : none

// Returns : true // Throws : none //

removeSlider = function () {

// unwind initialization and state

// remove DOM container; this removes event bindings too if ( jqueryMap.$slider ) {

jqueryMap.$slider.remove();

jqueryMap = {};

}

stateMap.$append_target = null;

stateMap.position_type = 'closed';

// unwind key configurations configMap.chat_model = null;

configMap.people_model = null;

configMap.set_chat_anchor = null;

return true;

};

// End public method /removeSlider/

// return public methods ...

We don’t try to get too clever with any remove method. The point is to lay waste to any prior configuration and initialization, and that’s it. We carefully ensure that data pointers are removed. This is important so that reference counts to data structures can drop to 0, which allows garbage collection to do its job. This is one reason why we always list potential configMap and stateMap keys at the top of the module—so we can see what we need to clean up.

We can test the removeSlider method by opening the Chrome Developer Tools JavaScript console and entering the following (don’t forget to press Return!):

spa.chat.removeSlider();

When we inspect the browser window we can see the chat slider has been removed. If we want to get it back, we can enter the following lines into the JavaScript console:

spa.chat.configModule({ set_chat_anchor: function (){ return true; } });

spa.chat.initModule( $( '#spa') );

The chat slider we “restored” with the JavaScript console isn’t fully functional, as we have provided a null function for the set_chat_anchor callback. In real use, we would always reenable the chat module from the Shell where we have access to the required callback.

We could do a lot more with this method—like having the slider disappear grace- fully—but we’ll leave that as an exercise for the reader. Let’s now implement another method which is commonly required by feature modules, handleResize.

4.5.2 The handleResize method

The second method common to many feature modules is handleResize. With good use of CSS, most content in an SPA can be made to work within a window that’s a reasonable size. But there are some cases where most doesn’t work and some recalculation is required. Let’s first implement the handleResize method as shown in listing 4.19 and then discuss its use. Changes are shown in bold:

...

configMap = { ...

slider_opened_em : 18, ...

slider_opened_min_em : 10, window_height_min_em : 20, ...

}, ...

// Begin DOM method /setPxSizes/

setPxSizes = function () {

var px_per_em, window_height_em, opened_height_em;

px_per_em = getEmSize( jqueryMap.$slider.get(0) );

window_height_em = Math.floor(

( $(window).height() / px_per_em ) + 0.5 );

opened_height_em

= window_height_em > configMap.window_height_min_em ? configMap.slider_opened_em

: configMap.slider_opened_min_em;

stateMap.px_per_em = px_per_em;

stateMap.slider_closed_px = configMap.slider_closed_em * px_per_em;

stateMap.slider_opened_px = opened_height_em * px_per_em;

jqueryMap.$sizer.css({

height : ( opened_height_em - 2 ) * px_per_em });

};

// End DOM method /setPxSizes/

...

// Begin public method /handleResize/

// Purpose :

// Given a window resize event, adjust the presentation // provided by this module if needed

// Actions :

Listing 4.19 Add the handleResize method—spa/js/spa.chat.js Increase the height of the opened

slider a bit. Add configuration for a minimum opened slider height.

Add configuration for the threshold widow height. If the window height is less than the threshold, we want to set the slider to the minimized height. If the height is greater than or equal to the threshold, we want to set the slider to the normal height.

Calculate window height in em units.

Here is the

“secret sauce” where we determine the slider opened height by comparing the current window height to the threshold.

Add the handleResize documentation and method.

135 Add frequently needed methods

// If the window height or width falls below

// a given threshold, resize the chat slider for the // reduced window size.

// Returns : Boolean

// * false - resize not considered // * true - resize considered // Throws : none

//

handleResize = function () {

// don't do anything if we don't have a slider container if ( ! jqueryMap.$slider ) { return false; }

setPxSizes();

if ( stateMap.position_type === 'opened' ){

jqueryMap.$slider.css({ height : stateMap.slider_opened_px });

}

return true;

};

// End public method /handleResize/

// return public methods ...

The handleResize event doesn’t call itself. Now we might be tempted to implement a window.resize event handler for every feature module, but that would be a bad idea.

The trouble is that the frequency at which a window.resize event fires varies wildly by browser. Let’s say we have five feature modules, all of which have window.resize event handlers, and our user decided to resize the browser. If the window.resize event fires every 10 milliseconds, and the resulting graphical changes are sufficiently complex, this can easily bring an SPA—and possibly the entire browser and OS it’s running on—

to its knees.

A better approach is to have a Shell event handler capture resize events and then have it call all subordinate feature module handleResize methods. This allows us to throttle the resize handling and dispatch from one event handler. Let’s implement this strategy in the Shell as shown in listing 4.20. Changes are shown in bold:

...

//--- BEGIN MODULE SCOPE VARIABLES --- var

configMap = { ...

resize_interval : 200, ...

},

stateMap = {

$container : undefined, anchor_map : {}, resize_idto : undefined },

jqueryMap = {},

Listing 4.20 Add the onResize event handler—spa/js/spa.shell.js

Recalculate the pixel sizes each time the handleResize method is called.

Ensure the slider height is set to the value calculated in setPxSizes if it’s extended during resize.

Create a 200-millisecond interval in our settings to consider resize events.

Set up a state variable to retain the resize timeout ID (see more later in this section).

copyAnchorMap, setJqueryMap,

changeAnchorPart, onHashchange, onResize, setChatAnchor, initModule;

//--- END MODULE SCOPE VARIABLES --- ...

//--- BEGIN EVENT HANDLERS --- ...

// Begin Event handler /onResize/

onResize = function (){

if ( stateMap.resize_idto ){ return true; } spa.chat.handleResize();

stateMap.resize_idto = setTimeout(

function (){ stateMap.resize_idto = undefined; }, configMap.resize_interval

);

return true;

};

// End Event handler /onResize/

//--- END EVENT HANDLERS --- ...

initModule = function (){

...

$(window)

.bind( 'resize', onResize )

.bind( 'hashchange', onHashchange ) .trigger( 'hashchange' );

};

// End PUBLIC method /initModule/

...

We want to adjust our stylesheet so we can better see the fruits of our labor. In listing 4.21 we adjust spa.css to decrease the minimal window size, move to relative units, and remove the gratuitous border around the content. Changes are shown in bold:

...

/** Begin reset */

* {

margin : 0;

padding : 0;

-webkit-box-sizing : border-box;

-moz-box-sizing : border-box;

box-sizing : border-box;

}

h1,h2,h3,h4,h5,h6,p { margin-bottom : 6pt; } ol,ul,dl { list-style-position : inside;}

/** End reset */

/** Begin standard selectors */

body {

Listing 4.21 Style changes to emphasize onResize—spa/css/spa.css Run the

onResize logic only if no resize timer is currently running.

The timeout function clears its own timeout ID, so once every 200 milliseconds during a resize, stateMap.resize_idto will be undefined, and the full onResize logic will run.

Return true from the window.resize event handler so that jQuery doesn’t preventDefault() or stopPropagation().

Bind the

window.resize event.

Move to relative units for margin (points).

Move to relative units on font size (points).

137 Summary

font : 10pt 'Trebuchet MS', Verdana, Helvetica, Arial, sans-serif;

...

/** End standard selectors */

/** Begin spa namespace selectors */

#spa {

position : absolute;

top : 0;

left : 0;

bottom : 0;

right : 0;

background : #fff;

min-height : 15em;

min-width : 35em;

overflow : hidden;

}

/** End spa namespace selectors */

/** Begin utility selectors */

...

We can now watch the resize event work by opening our browser document (spa/

spa.html) and then increasing or decreasing the browser window height. Figure 4.14 compares the slider presentation before and after the threshold has been reached:

Of course, there’s always room for more flourish. A nice enhancement would be to have the slider maintain a minimum distance from the top border. For example, if the window were 0.5 em over the threshold, the slider could be made to be precisely 0.5 em shorter than normal. This would provide a better user experience with optimal chat space and a smoother adjustment during resizing. The implementation isn’t hard and is left as an exercise for the reader.

Một phần của tài liệu single page web applications (Trang 157 - 162)

Tải bản đầy đủ (PDF)

(433 trang)