Render the feature containers

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

The layout document (spa/layout.html) we created is a nice foundation. Now we’re going to use it in our SPA. The first step is to have the Shell render the containers instead of using static HTML and CSS.

3.4.1 Convert the HTML to JavaScript

We’ll need our JavaScript to manage all our document changes; therefore, we need to convert the HTML developed earlier into a JavaScript string. We’ll keep the HTML indentation to ease legibility and maintainability as shown in listing 3.8:

Figure 3.4 HTML and CSS for containers—spa/layout.html

73 Render the feature containers

var main_html = String()

+ '<div class="spa-shell-head">'

+ '<div class="spa-shell-head-logo"></div>' + '<div class="spa-shell-head-acct"></div>' + '<div class="spa-shell-head-search"></div>' + '</div>'

+ '<div class="spa-shell-main">'

+ '<div class="spa-shell-main-nav"></div>' + '<div class="spa-shell-main-content"></div>' + '</div>'

+ '<div class="spa-shell-foot"></div>' + '<div class="spa-shell-chat"></div>' + '<div class="spa-shell-modal"></div>';

We aren’t worried about any performance penalty of concatenated strings. Once we get to production, the JavaScript minifier will join the string for us.

Listing 3.8 Concatenating the HTML template

Added class

Figure 3.5 Double-click in the HTML to add a class in Chrome Developer Tools

Configure your editor!

A professional developer should be using a professional-grade text editor or IDE. Most of these have regular expression and macro support. We should be able to automate converting HTML into a JavaScript string. For example, the venerable vim editor can be configured so that two keystrokes will format HTML into a JavaScript concatenated string. We can add the following to our ~/.vimrc file:

3.4.2 Add an HTML template to our JavaScript

It’s now time to take a bold step and create our Shell. When we initialize the Shell, we’d like to have it fill the page element of our choice with the feature containers.

While we’re at it, we’d like to cache the jQuery collection objects. We can use the module template from appendix A along with the JavaScript string we just created to accomplish this. Let’s fire up our text editor and create the file shown in listing 3.9.

Please pay careful attention to the annotations, as they provide useful details:

/*

* spa.shell.js

* Shell module for SPA

*/

/*jslint browser : true, continue : true, devel : true, indent : 2, maxerr : 50, newcap : true, nomen : true, plusplus : true, regexp : true, sloppy : true, vars : false, white : true

*/

/*global $, spa */

spa.shell = (function () {

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

configMap = {

main_html : String()

+ '<div class="spa-shell-head">'

+ '<div class="spa-shell-head-logo"></div>' + '<div class="spa-shell-head-acct"></div>' + '<div class="spa-shell-head-search"></div>' + '</div>'

+ '<div class="spa-shell-main">'

+ '<div class="spa-shell-main-nav"></div>' + '<div class="spa-shell-main-content"></div>' + '</div>'

+ '<div class="spa-shell-foot"></div>' + '<div class="spa-shell-chat"></div>' + '<div class="spa-shell-modal"></div>' },

stateMap = { $container : null }, jqueryMap = {},

setJqueryMap, initModule;

Listing 3.9 Starting the Shell—spa/js/spa.shell.js (continued)

vmap <silent> ;h :s?^\(\s*\)+ '\([^']\+\)',*\s*$?\1\2?g<CR>

vmap <silent> ;q :s?^\(\s*\)\(.*\)\s*$? \1 + '\2'?<CR>

Once we restart vim, we can visually select the HTML to change. When we press ;q the selection will be formatted; when we press ;h we will undo the format.

Declare all variables that are available across the namespace—spa.shell in this case—in the

“Module Scope” section.

See appendix A for a complete discussion of this and other sections in the template.

Place static configuration values in configMap.

Indent HTML strings. This aids comprehension and eases maintenance.

Place dynamic information shared across the module in stateMap. Cache jQuery

collections in jqueryMap. Declare all module

scope variables in this section. Many are assigned later.

75 Render the feature containers

//--- 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 };

};

// End DOM method /setJqueryMap/

//--- END DOM METHODS --- //--- BEGIN EVENT HANDLERS --- //--- END EVENT HANDLERS --- //--- BEGIN PUBLIC METHODS --- // Begin Public method /initModule/

initModule = function ( $container ) { stateMap.$container = $container;

$container.html( configMap.main_html );

setJqueryMap();

};

// End PUBLIC method /initModule/

return { initModule : initModule };

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

Now we have a module that renders the feature containers, but we still have to popu- late the CSS file and instruct the root namespace module (spa/js/spa.js) to use the Shell module (spa/js/spa.shell.js) instead of presenting the time-honored “hello world” text. Let’s get to it.

3.4.3 Write the Shell stylesheet

Using our handy namespacing conventions presented in appendix A, we know we need to put our spa-shell-* selectors into a file named spa/css/spa.shell.css. We can copy the CSS we developed in spa/layout.html directly into that file, as shown in listing 3.10:

/*

* spa.shell.css

* Shell styles

*/

.spa-shell-head, .spa-shell-head-logo, .spa-shell-head-acct, .spa-shell-head-search, .spa-shell-main, .spa-shell-main-nav, .spa-shell-main-content, .spa-shell-foot, .spa-shell-chat,

Listing 3.10 The Shell CSS, take 1—spa/css/spa.shell.css

Reserve the “Utility Methods”

section for functions that don’t interact with page elements.

Place functions that create and manipulate page elements in the “DOM Methods” section.

Use setJqueryMap to cache jQuery collections. This function should be in almost every shell and feature module we write. The use of the jqueryMap cache can greatly reduce the number of jQuery document transversals and improve performance.

Reserve an

"Event Handlers"

section for jQuery event handler functions.

Create the initModule public method, which will be used to initialize the module.

Place publicly available methods in the “Public Methods” section.

Export public methods explicitly by returning them in a map. At present only initModule is available.

.spa-shell-modal { position : absolute;

}

.spa-shell-head { top : 0;

left : 0;

right : 0;

height : 40px;

}

.spa-shell-head-logo {

top : 4px;

left : 4px;

height : 32px;

width : 128px;

background : orange;

}

.spa-shell-head-acct {

top : 4px;

right : 0;

width : 64px;

height : 32px;

background : green;

}

.spa-shell-head-search {

top : 4px;

right : 64px;

width : 248px;

height : 32px;

background : blue;

}

.spa-shell-main { top : 40px;

left : 0;

bottom : 40px;

right : 0;

}

.spa-shell-main-content, .spa-shell-main-nav {

top : 0;

bottom : 0;

}

.spa-shell-main-nav { width : 250px;

background : #eee;

}

.spa-x-closed .spa-shell-main-nav { width : 0;

}

.spa-shell-main-content { left : 250px;

right : 0;

background : #ddd;

}

.spa-x-closed .spa-shell-main-content {

Define shared CSS rules.

Use the parent classes to affect child elements. This is perhaps one of the most powerful capabilities of CSS, and not used nearly often enough.

Indent derived selectors and place immediately below the parent selector. Derived selectors are selectors clearly dependent on a parent for meaning.

77 Render the feature containers

left : 0;

}

.spa-shell-foot { bottom : 0;

left : 0;

right : 0;

height : 40px;

}

.spa-shell-chat { bottom : 0;

right : 0;

width : 300px;

height : 15px;

background : red;

z-index : 1;

}

.spa-shell-modal { margin-top : -200px;

margin-left : -200px;

top : 50%;

left : 50%;

width : 400px;

height : 400px;

background : #fff;

border-radius : 3px;

z-index : 2;

}

All Selectors have the spa-shell- prefix. This has multiple benefits:

■ It shows that these classes are controlled by the Shell module (spa/js/

spa.shell.js).

■ It prevents namespace collisions with third-party scripts and our other modules.

■ When we’re debugging and inspecting the document HTML, we can immedi- ately see which elements are generated and controlled by the shell module.

All of these benefits prevent us from descending into the fiery depths of CSS-selector- name-goulash hell. Anyone who’s ever managed stylesheets on even a moderate scale should know exactly what we’re talking about.

3.4.4 Direct the application to use the Shell

Now let’s modify our root namespace module (spa/js/spa.js) to use the Shell instead of slavishly copying “hello world” into the DOM. The following adjustment shown in bold should do the trick:

/*

* spa.js

* Root namespace module

*/

...

/*global $, spa */

var spa = (function () {

var initModule = function ( $container ) { spa.shell.initModule( $container );

};

return { initModule: initModule };

}());

We should now be able to open our browser document (spa/spa.html) and see some- thing similar to figure 3.6. We can use Chrome Developer Tools to confirm the document generated by our SPA (spa/spa.html) matches our layout document (spa/layout.html).

With this foundation in place, we’ll begin the work to have the Shell manage the fea- ture containers. It might also be a good time to take a break, as the next section is fairly ambitious.

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

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

(433 trang)