JavaScript Bible 5th Edition 2004 phần 10 ppt

168 208 0
JavaScript Bible 5th Edition 2004 phần 10 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

BC399 Chapter 52 ✦ Application: Outline-Style Table of Contents ((i== ol.childNodes.length-1) ? collapsedWidgetEnd : (blockID==0) ? collapsedWidgetStart : collapsedWidget); output += “‘ height=” + widgetHeight + “ width=” + widgetWidth; output += “ title=’Click to expand/collapse nested items.’ onclick=’toggle(this,” + blockID + “)’>”; link = (ol.childNodes[i].getAttribute(“uri”)) ? ol.childNodes[i].getAttribute(“uri”) : “”; if (link) { output += “&nbsp;<a href=’” + link + “‘ class=’itemTitle’ title=’” + link + “‘ target=’” + displayTarget + “‘>”; } else { output += “&nbsp;<a class=’itemTitle’ title=’” + link + “‘>”; } output += “&nbsp;” + ol.childNodes[i].getAttribute(“text”) + “</a>”; currState += calcBlockState(outlineID, currID-1); output += “<span class=’OLBlock’ blocknum=’” + blockID + “‘ id=’OLBlock” + blockID++ + “‘>”; nestPrefix = prefix; nestPrefix += (i == ol.childNodes.length - 1) ? “<img src=’” + emptySpace + “‘ height=16 width=20>” : “<img src=’” + chainSpace + “‘ height=16 width=20>”; output += makeHTML(outlineID, ol.childNodes[i], nestPrefix); output += “</span></div>\n”; } else { output += prefix; output += “<img id=’widget” + (currID-1) + “‘ src=’” + ((i == ol.childNodes.length - 1) ? nodeWidgetEnd : nodeWidget); output += “‘ height=” + widgetHeight + “ width=” + widgetWidth + “>”; link = (ol.childNodes[i].getAttribute(“uri”)) ? ol.childNodes[i].getAttribute(“uri”) : “”; if (link) { output += “&nbsp;<a href=’” + link + “‘ class=’itemTitle’ title=’” + link + “‘ target=’” + displayTarget + “‘>”; } else { output += “&nbsp;<a class=’itemTitle’ title=’” + link + “‘>”; } output += ol.childNodes[i].getAttribute(“text”) + “</a>”; output += “</div>\n”; } } return output; } As with the HTML assembly code of the first outliner, if you were to add attributes to outline elements in an OPML outline (for example, a URL for an icon to display in front of the text), it is in makeHTML() that the values would be read and applied to the HTML being created. The only other function invoked by the makeHTML() function is calcBlockState(). This function looks into one of the OPML outline’s head elements, called expansionstate. This element’s values can be set to a comma-delimited list of numbers corresponding to nodes that are to be shown expanded when the outline is first displayed. The calcBlockState() function is invoked for each parent element. The element’s location is compared against values in the expansionstate element, if there are any, and returns the appropriate 1 or 0 value for the state string being assembled for the rendered outline: BC400 Part VI ✦ Bonus Chapters // apply default expansion state from outline’s header // info to the expanded state for one element to help // initialize currState variable function calcBlockState(outlineID, n) { var ol = document.getElementById(outlineID).getElementsByTagName(“body”)[0]; var outlineLen = ol.getElementsByTagName(“outline”).length; // get OPML expansionState data var expandElem = document.getElementById( outlineID).getElementsByTagName(“expansionState”)[0]; var expandedData = (expandElem.childNodes.length) ? expandElem.firstChild.nodeValue.split(“,”) : null; if (expandedData) { for (var j = 0; j < expandedData.length; j++) { if (n == expandedData[j] - 1) { return “1”; } } } return “0”; } The final act of the initialization process is a call to the initExpand() function. This function loops through the currState global variable (whose value was written in makeHTML() with the help of calcBlockState()) and sets the display property to block for any element designed to be expanded at the outset. HTML element construction in makeHTML() is performed in such a way that each parent div has a span nested directly inside of it; and inside that span are all the child nodes. The display property of the span determines whether all of those children are seen or not. // expand items set in expansionState XML tag, if any function initExpand(outlineID) { for (var i = 0; i < currState.length; i++) { if (currState.charAt(i) == 1) { document.getElementById(“OLBlock” + i).style.display = “block”; } } } By the time the initExpand() function has run — a lot of setup code that executes pretty quickly — the rendered outline is in a steady state. Users can now expand or collapse portions by clicking the widget icons. Toggling node expansion All of the widget images in the outline have onclick event handlers assigned to them. The handlers invoke the toggle() function, passing parameters consisting of a reference to the img element object receiving the event and the serial number of the span block nested just inside the div that holds the image. With these two pieces of information, the toggle() func- tion sets in motion the act of inverting the expanded/collapsed state of the element and the plus or minus version of the icon image. The blockNum parameter corresponds to the position within the currState string of 1s and 0s holding the flag for the expanded state of the block. With the current value retrieved from currState, the value is inverted through swapState(). Then, based on the new setting, the display property of the block is set accordingly, and widget art is changed through two special-purpose functions: BC401 Chapter 52 ✦ Application: Outline-Style Table of Contents // toggle an outline mother entry, storing new state value; // invoked by onclick event handlers of widget image elements function toggle(img, blockNum) { var newString = “”; var expanded, n; // modify state string based on parameters from img expanded = currState.charAt(blockNum); currState = swapState(currState, expanded, blockNum); // dynamically change display style if (expanded == “0”) { document.getElementById(“OLBlock” + blockNum).style.display = “block”; img.src = getExpandedWidgetState(img.src); } else { document.getElementById(“OLBlock” + blockNum).style.display = “none”; img.src = getCollapsedWidgetState(img.src); } } Swapping the state of the currState variable utilizes the same XOR operator employed by the first outliner in this chapter. The entire currState string is passed as a parameter. The indicated digit is segregated and inverted, and the string is reassembled before being returned to the calling statement in toggle(): // invert state function swapState(currState, currVal, n) { var newState = currState.substring(0,n); newState += currVal ^ 1; // Bitwise XOR item n newState += currState.substring(n+1,currState.length); return newState; } As mentioned earlier, each of the clickable widget icons (plus and minus) can be one of three states, depending on whether the widget is at the start, middle, or end of a vertical-dotted chain. The two image swapping functions find out (by inspecting the URLs of the images cur- rently occupying the img element) which version is currently in place so that, for instance, a starting plus (collapsed) widget is replaced with a starting minus (expanded) widget. This is a case of going the extra mile for the sake of an improved user interface: // retrieve matching version of ‘minus’ images function getExpandedWidgetState(imgURL) { if (imgURL.indexOf(“Start”) != -1) { return expandedWidgetStart; } if (imgURL.indexOf(“End”) != -1) { return expandedWidgetEnd; } return expandedWidget; } // retrieve matching version of ‘plus’ images function getCollapsedWidgetState(imgURL) { if (imgURL.indexOf(“Start”) != -1) { return collapsedWidgetStart; } if (imgURL.indexOf(“End”) != -1) { return collapsedWidgetEnd; } return collapsedWidget; } BC402 Part VI ✦ Bonus Chapters Wrap up There’s no question that the amount and complexity of the code involved for the OPML version of the outliner are significant. The “pain” associated with developing an application such as this is all up front. After that, the outline content is easily modifiable in the OPML format (or perhaps by some future editor that produces OPML output). Even if you don’t plan to implement an OPML outline, the explanation of how this example works should drive home the importance of designing data structures that assist not only the visual design but also the scripting that you use to manipulate the visual design. Further Thoughts The advent of CSS and element positioning has prompted numerous JavaScripters to develop another kind of hierarchical system of pop-up or drop-down menus. You can find examples of this interface at many of the JavaScript source Web sites listed in Appendix D. Making these kinds of menus work well in NN4, IE4+, and NN6+/W3C DOM browsers is a lot of hard work, and if you can adopt code already ironed out by others, then all the better. Most of the code you find, however, will require a fair amount of tweaking to blend the function- ality into the visual design that you have or are planning for your Web site. Finding two imple- mentations on the Web that look or behave the same way is rare. As long as you’re aware of what you’ll be getting yourself into, you are encouraged to check out these interface elements. By hiding menu choices except when needed, valuable screen real estate is preserved for more important, static content. ✦✦✦ Application: Calculations and Graphics W hen the scripting world had its first pre-release peeks at JavaScript (while Netscape was still calling the language LiveScript), the notion of creating interactive HTML-based calculators captured the imaginations of many page authors. Somewhere on the World Wide Web, you can find probably every kind of special-purpose calculation normally done by scientific calculators and personal com- puter programs—leaving only weather-modeling calculations to the supercomputers of the world. In the search for my calculator gift to the JavaScript universe, I looked around for something more graphical. Numbers, by themselves, are pretty boring; so any way the math could be enlivened was fine by me. Having been an electronics hobbyist since I was a kid, I recalled the color-coding of electronic resistor components. The values of these gizmos aren’t printed in plain numbers anywhere. You have to know the code and the meaning of the location of the colored bands to arrive at the value of each one. I thought that this calculator would be fun to play with, even if you don’t know what a resistor is. 53 53 CHAPTER ✦✦✦✦ In This Chapter Precached images Math calculations CGI-like image assembly ✦✦✦✦ BC404 Part VI ✦ Bonus Chapters The Calculation To give you an appreciation for the calculation that goes into determining a resistor’s value, here is the way the system works. Three closely spaced color bands determine the resistance value in ohms. The first (leftmost) band is the tens digit; the second (middle) band is the ones digit. Each color has a number from 0 through 9 assigned to it (black = 0, brown = 1, and so on). Therefore, if the first band is brown and the second band is black, the number you start off with is 10. The third band is a multiplier. Each color determines the power of ten by which you multiply the first digits. For example, the red color corresponds to a multiplier of 102, so that 10 × 102 equals 1,000 ohms. A fourth band, if present, indicates the tolerance of the component — how far, plus or minus, the resistance measurement can fluctuate due to variations in the manufacturing process. Gold means a tolerance of plus-or-minus 5 percent; silver means plus-or-minus 10 percent; and no band means a 20 percent tolerance. A pinch of extra space typically appears between the main group of three-color bands and the one tolerance band. User Interface Ideas The quick-and-dirty, non-graphical approach for a user interface was to use a single frame with four select elements defined as pop-up menus (one for each of the four color bands on a resistor), a button to trigger calculation, and a field to show the numeric results. How dull. It occurred to me that if I design the art carefully, I can have JavaScript assemble an updated image of the resistor consisting of different slices of art: static images for the resistor’s left and right ends, and variable slivers of color bands for the middle. Rather than use the brute force method of creating an image for every possible combination of colors (3,600 images total!), a far more efficient approach is to have one image file for each color (12 colors plus 1 empty) and enable JavaScript to call them from the server, as needed, in the proper order. If not for client-side JavaScript, a CGI script on the server would have to handle this kind of intelligence and user interaction. But with this system, any dumb server can dish up the image files as called by the JavaScript script. The first generation of this resistor calculator used two frames, primarily because I needed a second frame to update the calculator’s art dynamically while keeping the pop-up color menus stationary. Images couldn’t be swapped back in those frontier days, so the lower frame had to be redrawn for each color choice. Fortunately, modern browsers enabled me to update indi- vidual image objects in a loaded document without any document reloading. Moreover, with all the images precached in memory, page users experience no (or virtually no) delay in making a change from one value to another. The design for the new version is a simple, single-document interface (see Figure 53-1). Four pop-up menus let you match colors of a resistor, whereas the onchange event handler in each menu automatically triggers an image and calculation update. To hold the art together on the page, a table border surrounds the images on the page, whereas the numeric value of the component appears in a text field. BC405 Chapter 53 ✦ Application: Calculations and Graphics Figure 53-1: The Resistor Calculator with images inside a table border. The Code All the action takes place in the file named resistor.htm. A second document is an introduc- tory HTML text document that explains what a resistor is and why you need a calculator to determine a component’s value. The article, called The Path of Least Resistance, can be viewed in a secondary window from a link in the main window. Here you will be looking only at resistor.htm, which has been updated to include stylesheets. The document begins in the traditional way without any surprises: <html> <head> <title>Graphical Resistance Calculator</title> <style type=”text/css”> body {font-family:Arial, Helvetica, serif} </style> <script type=”text/javascript”> BC406 Part VI ✦ Bonus Chapters Basic arrays In calculating the resistance, the script needs to know the multiplier value for each color. If not for the last two multiplier values actually being negative multipliers (for example, 10-1 and 10-2), I could have used the index values without having to create this array. But the two out-of-sequence values at the end make it easier to work with an array rather than to try spe- cial-casing these instances in later calculations: // create array listing all the multiplier values var multiplier = new Array(); multiplier[0] = 0; multiplier[1] = 1; multiplier[2] = 2; multiplier[3] = 3; multiplier[4] = 4; multiplier[5] = 5; multiplier[6] = 6; multiplier[7] = 7; multiplier[8] = 8; multiplier[9] = 9; multiplier[10] = -1; multiplier[11] = -2; // create array listing all tolerance values var tolerance = new Array(); tolerance[0] = “+/-5%”; tolerance[1] = “+/-10%”; tolerance[2] = “+/-20%”; Although the script doesn’t do any calculations with the tolerance percentages, it needs to have the strings corresponding to each color for display in the pop-up menu. The tolerance array is there for that purpose. Calculations and formatting Before the script displays the resistance value, it needs to format the numbers in values that are meaningful to those who know about these values. Just as measures of computer storage bytes, high quantities of ohms are preceded with “kilo” and “meg” prefixes, commonly abbre- viated with the “K” and “M” letters. The format() function determines the order of magnitude of the final calculation (from another function shown in the following section) and formats the results with the proper unit of measure: // format large values into kilo and meg function format(ohmage) { if (ohmage >= 1e6) { ohmage /= 1e6; return “” + ohmage + “ Mohms”; } else { if (ohmage >= 1e3) { ohmage /= 1e3; return “” + ohmage + “ Kohms”; } else { return “” + ohmage + “ ohms”; } } } BC407 Chapter 53 ✦ Application: Calculations and Graphics The selections from the pop-up menus meet the calculation formulas of resistors in the calcOhms() function. Because this function is triggered indirectly by each of the select objects, sending any of their parameters to the function is a waste of effort. Moreover, the calcOhms() function is invoked by the onload event handler, which is not tied to the form or its controls. Therefore, the function obtains the reference to the form and then extracts the necessary values of the four select objects by using explicit (named) references. Each value is stored in a local variable for convenience in completing the ensuing calculation. Recalling the rules used to calculate values of the resistor bands, the first statement of the calculation multiplies the “tens” pop-up value times 10 to determine the tens digit and then adds the ones digit. From there, the combined value is multiplied by the exponent value of the selected multiplier value. Notice that the expression first assembles the value as a string to concatenate the exponent factor and then evaluates it to a number. Although I try to avoid the eval() function because of its slow performance, the one call per calculation is not a per- formance issue at all. The evaluated number is passed to the format() function for proper for- matting (and setting of order of magnitude). In the meantime, the tolerance value is extracted from its array, and the combined string is plugged into the result text field (which is in a sepa- rate form, as described later): // calculate resistance and tolerance values function calcOhms() { var form = document.forms[“input”]; var d1 = form.tensSelect.selectedIndex; var d2 = form.onesSelect.selectedIndex; var m = form.multiplierSelect.selectedIndex; var t = form.toleranceSelect.selectedIndex; var ohmage = (d1 * 10) + d2; ohmage = eval(“” + ohmage + “e” + multiplier[m]); ohmage = format(ohmage); var tol = tolerance[t]; document.forms[“output”].result.value = ohmage + “, “ + tol; } Preloading images As part of the script that runs when the document loads, the next group of statements pre- caches all possible images that can be displayed for the resistor art. For added scripting con- venience, the color names are loaded into an array. With the help of that just-created array of color names, you then create another array ( imageDB), which both generates Image objects for each image file and assigns a URL to each image. Notice an important subtlety about the index values being used to create each entry of the imageDB array: Each index is a colorArray entry, which is the name of the color. As you found out in Chapter 30, if you create an array element with a named index, you must use that style of index to retrieve the data thereafter; you cannot switch arbitrarily between numeric indexes and names. As you see in a moment, this named index provides a critical link between the choices a user makes in the pop-up lists and the image objects being updated with the proper image file. // pre-load all color images into image cache var colorArray = new Array(“Black”,”Blue”,”Brown”,”Gold”,”Gray”, “Green”,”None”,”Orange”,”Red”,”Silver”,”Violet”,”White”,”Yellow”); var imageDB = new Array(); for (i = 0; i < colorArray.length; i++) { imageDB[colorArray[i]] = new Image(21,182); imageDB[colorArray[i]].src = colorArray[i] + “.gif”; } BC408 Part VI ✦ Bonus Chapters The act of assigning a URL to the src property of an Image object instructs the browser to pre-load the image file into memory. This pre-loading happens as the document is loading, so another few seconds of delay won’t be noticed by the user. Changing images on the fly The next four functions are invoked by their respective select object’s onchange event handler. For example, after a user makes a new choice in the first select object (the “tens” value color selector), that select object reference is passed to the setTens() function. Its job is to extract the text of the choice and use that text as the index into the imageDB array. Alternatively, the color name can also be assigned to the value attribute of each option, and the value property read here. You need this connection to assign the src property of that array entry to the src property of the image that you see on the page (defined in the following section). This assignment is what enables the images of the resistor to be updated instantaneously — just the one image “slice” affected by the user’s choice in a select object: function setTens(choice) { var tensColor = choice.options[choice.selectedIndex].value; document.tens.src = imageDB[tensColor].src; calcOhms(); } function setOnes(choice) { var onesColor = choice.options[choice.selectedIndex].value; document.ones.src = imageDB[onesColor].src; calcOhms(); } function setMult(choice) { var multColor = choice.options[choice.selectedIndex].value; document.mult.src = imageDB[multColor].src; calcOhms(); } function setTol(choice) { var tolColor = choice.options[choice.selectedIndex].value; document.tol.src = imageDB[tolColor].src; calcOhms(); } The rest of the script for the Head portion of the document merely provides the statements that open the secondary window to display the introductory document: function showIntro() { window.open(“resintro.htm”,””,”width=400,height=320, left=100,top=100”); } </script> </head> Creating the select objects A comparatively lengthy part of the document is consumed with the HTML for the four select objects. Notice, however, that the document contains an onload event handler in the <body> tag. This handler calculates the results of the currently selected choices whenever the docu- ment loads or reloads. If it weren’t for this event handler, you would not see the resistor art [...]... bgcolor=”white”> Gold Silver None BC409 BC 410 Part VI ✦ Bonus Chapters Drawing the initial images The balance of the document, mostly in JavaScript, is devoted to creating the table and image objects whose src properties will be modified with each choice of a select object The act of assembling... functions, making them easy to incorporate into this script Because many of the entry fields on two screens must be integers between 1 and 100 , I brought the data validation functions to the parent document rather than duplicating them in each of the subdocuments: // JavaScript sees numbers with leading zeros as octal values, so strip // zeros function stripZeros(inputStr) { var result = inputStr; while... loads, without a ton of unwanted white space if the browser window is large: It’s really cool but... browsers, so to let these image maps work with older versions, mouse action is converted to script action by assigning a javascript: pseudo-URL to the HREF attribute for each area element Instead of pointing to an entirely new URL (as area elements usually work), the attributes point to the JavaScript functions defined in the Head portion of this document After a user clicks the rectangle specified by an... save that moment in time so that it can be the comparison measure for the next visit Fortunately, whenever you create a new date object in JavaScript, it does so internally as the GMT date and time Even though the way you attempt to read the date and time created by JavaScript shows you the results in your computer’s local time, the display is actually filtered through the time zone offset as directed... following listing) are all assigned the same name, so that JavaScript lets you treat them as array objects — greatly simplifying the placement of values into those fields inside a for loop: DH2 body {font-family:Arial, Helvetica, serif} h2 {font-size:18px} h4 {font-size:14px} function loadAlternatives() { for (var... date of current visit (in GMT time) to cookie function saveCurrentVisit() { var visitDate = new Date(); var nowGMT = visitDate.getTime() - dateAdjustment; var expires = (nowGMT + (180 * 24 * 60 * 60 *100 0)); expires = new Date(expires); expires = expires.toGMTString(); setCookieData(“lastVisit”, nowGMT, expires); } From the current time, I create an expiration date for the cookie The example shows . is 10. The third band is a multiplier. Each color determines the power of ten by which you multiply the first digits. For example, the red color corresponds to a multiplier of 102 , so that 10. has prompted numerous JavaScripters to develop another kind of hierarchical system of pop-up or drop-down menus. You can find examples of this interface at many of the JavaScript source Web sites. each color (12 colors plus 1 empty) and enable JavaScript to call them from the server, as needed, in the proper order. If not for client-side JavaScript, a CGI script on the server would have

Ngày đăng: 12/08/2014, 19:21

Tài liệu cùng người dùng

Tài liệu liên quan