JavaScript: Color ChooserColor ChooserTable of Contents:
In this chapter, you learn how to create a color chooser, a tool that lets you try combinations of colors on a Web page without having to edit and re-edit the page. You can specify five global colors in a Web page\'s tag. The five colors stipulate which colors the browser will use when it displays the Web page, and you specify them in attributes of the Web page\'s tag. The BGCOLOR attribute specifies the color of the page\'s background. The TEXT attribute specifies the default color of the text in the page. The LINK attribute specifies the color of the text and border of link elements the user has not yet visited. The ALINK attribute specifies the color of the active link, which is the link element that the user has just clicked on. Finally, the VLINK attribute specifies the color of the text of link elements that the user has visited. All colors are specified as 24-bit values-8 bits for the red component, 8 bits for the green component, and 8 bits for the blue component. The three colors are always specified in that order-red, green, blue-and the overall color is an RGB (red, green, blue) value. Not all video systems can directly support 24-bit colors; for such systems, applications such as Netscape must either use approximations of the 24-bit value or must dither the color. Dithering involves mixing pixels of different colors to fool the eye into seeing the illusion of a 24-bit color. Either way, two colors that are technically different may appear to be identical on some systems. You can specify the color values you use in your Web page by using RGB values. An RGB value is a string consisting of a pound sign (#) followed by a two-digit hexadecimal (base 16) value for the red component, a two-digit hexadecimal value for the green component, and a two-digit hexadecimal value for the blue component. For example, a value of #1E90FFdescribes a color whose red component is 1E, whose green component is 90, and whose blue component is FF. It is very blue, with a good bit of green to it, and a little bit of red. Netscape calls this color "dodgerblue." Including dodgerblue, Netscape has named 140 colors. Instead of using RGB
values, you can use one of the named colors, and you can use a named color
anywhere you can use an RGB value.
The scenarioA color chooser allows the user to specify colors for the five color attributes. Such a tool lets you try out combinations of colors to see what they look like together. Perhaps two colors are too similar and you can\'t tell one from the other, or maybe one of the colors cannot be distinguished from the background color. You\'ll be able to pick out these problems when you use a color chooser. The color chooser lets you experiment without showing your failures to the world. The requirementsWhat should a color chooser do? For starters, it should let you use Netscape\'s named colors, such as dodgerblue or floralwhite. It should also let you use RGB values-after all, the 140 named colors only represent a tiny fraction of the nearly seventeen million colors that you can define with RGB values. The color chooser should let you fine-tune the colors, making a given color just a little more red, or maybe a little less green. It must let you specify colors for each of the five color attributes. It has to let you see a sample page with the colors you\'ve specified. And it should show you what the tag looks like with the colors you\'ve selected, so you can use the tag in a Web page. The solution (without JavaScript)Because you need to be able to specify colors for each of the five color attributes, you need a form with five input fields. These fields should be text fields, so you can enter either an RGB value or a named field. Each field needs to be large enough to hold the longest named color (20 characters, for lightgoldenrodyellow). The form needs a submit button; it should also have a reset button-that\'s just good form etiquette. A simple color chooser How good a solution is this? Let\'s measure it against the requirements. Can you use named colors? Yes, although you run the risk of making typos. Can you use RGB values? Again, yes, with the risk of mistyping them. Can you fine-tune colors? Yes, if they\'re RGB values. To fine-tune named colors, you need the documentation in front of you to get the RGB value of the named color. Can you set each of the tag\'s five color attributes? Yes, you can. Can you see what the colors look like together? Whoops. No, not with just the code in Listing 6.1. You also need a CGI (Common Gateway Interface) program back on the server. The CGI program has to get the string generated when you press the submit button, parse it into the colors for each of the attributes, construct a valid HTML page that uses the five attributes, and send that page back as a response. It should also handle the last requirement, by including the tag attributes in the displayed text. You can\'t solve this problem with HTML alone, without JavaScript. You must have the CGI program as well. Unfortunately, some Web servers won\'t let you write your own CGI programs and run them from their system. The language you use is determined by what your Web server supports, and if you decide to change Web servers, you may have to start over from scratch-the new server may not support your CGI program. The solution (with JavaScript)In addition to the problems intrinsic to writing CGI (the server may not allow CGI programs, CGI programs are not necessarily portable from one server to another, a CGI program on a slow, bogged-down server may be very slow), there are bandwidth considerations. Every time you press the See Result button, your selections are passed through the network back to the server. Then the server sends back a new page. This is a waste of bandwidth, and makes you vulnerable to the rising amount of traffic on the Internet: The browser may well time out before it gets the result from the server. With JavaScript, you can do much better. You can eliminate the need for the CGI program by breaking the window into two frames. You place the form in one frame and use the form\'s field contents to draw a new document in the second frame. This eliminates the repeated, and unnecessary, traffic between the users\' browsers and the server. You need three documents now. The first document is a frame document that splits the window into frames, as shown here: You also need a blank document to load into the ViewScreen frame. Here is such a document: Finally, you need the document that creates the form and processes the data in the form. This document is described in the next section. The FormUsing JavaScript, you can make the form much easier to use and make it impossible for the user to enter an illegal color value. First, let\'s divide the top frame into three parts. In the first part, the user specifies a color. In the second part, the user applies the selected color to one or more of the tag\'s five color attributes. In the third part are the See Result and Ugh! Start Over! buttons. Selecting a colorIn the top part of the page, let\'s use a table to lay out the fields for selecting a color. The table is divided into separate sections: one for the red component, one for the blue component, and one for the green component. There\'s also one more section for entering a named color. A text field for setting the color level makes sense, and you can add buttons to increment and decrement the color level-that should suffice for fine-tuning. The text field should be used for entering a hexadecimal value for the color\'s component of the RGB color value, but let\'s add another text field. Some computer systems express colors in RGB format but do so by using decimal values separated by commas, as in 255,0,255 (equivalent to #FF00FF). Let\'s use the other text field to handle decimal values. You\'ll also need some text to let the user know which set of fields are for which color, and you\'ll need some table headings to let the user know what to do with the text fields and buttons. To create the table shown in Figure 6.4, you need to create a TABLE element using the HTML code in Listing 6.2. Creating the color selection table The input fields in this table need some event handlers so that, when the user does something with each field, you can do something useful with the input. You need ONCHANGE event handlers for the TEXT fields; they\'ll be called when the user enters a new value in the TEXT field and moves focus to another field. You need ONCLICK event handlers for the BUTTON fields; they are called when the user clicks on a button. And, finally, you need an ONCHANGE event handler for the SELECT field; it is called when the user selects a new value and moves focus to another field. There are six TEXT fields, six BUTTON fields, and a SELECT field, so you\'ll need a total of 13 event handler functions. Wrong! You need five event handler functions. Why? Think about it: What\'s the difference between the event handler that handles a change in the red decimal field and the event handler that handles a change in the green decimal field? Very little; they have to do the same thing, except that one pertains to the red component and one pertains to the green component. The code doesn\'t have to be different. To create an RGB color one color at a time, you need to store each of the three components separately. To do that, let\'s create three global variables for them: var redness = Ø; Now let\'s give each of the TEXT and BUTTON fields names. The names will be based on which color they deal with and what they do with it, so the six TEXT fields are named "redDec," "redHex," "greenDec," "greenHex," "blueDec," and "blueHex." The six BUTTON fields are named "redPlus," "redMinus," "greenPlus," "greenMinus," "bluePlus," and "blueMinus." Giving the fields names tied to their colors allows you to combine similar event handlers. Here is the handler for the change event on the decimal TEXT fields: function readDecimal(text) The readDecimal function is specified in the tag as ONCHANGE="readDecimal(this)" passing a reference to the TEXT field as the single parameter. The TEXT field\'s name is available as its name property. You want the color part, which is the part of the name preceding the suffix Dec. To get the color portion, you use the substring method to extract the substring from the first character of the string to the first character of the Dec suffix. Having the color name (red, green, or blue), you now need the value. You get the color by using the built-in function parseInt() on the TEXT field\'s value, which is obtained by using its value property. If the color is out of range-less than 0 or greater than 255-an alert window is displayed, telling the user that the value entered is rejected, and the function returns. Otherwise, a new function, setColor(), is called with the name and value of the color. Similarly, you need a function to handle the change event on the hexadecimal TEXT fields. readHex() ONCHANGE event handler function readHex(text) Notice that this function is exactly like readDecimal, except for one difference: The value is appended to the string "0x" in the call to parseInt(). This forces parseInt() to interpret the characters in the value as hexadecimal characters. Finally, let\'s look at the event handlers for the plus (>>) and minus (<<) BUTTON fields. plus() ONCLICK event handler function plus(text) This function is also similar to the readDecimal() and readHex() functions. The difference is in how it acquires the color value. It creates a string containing the color name and the string "ness" (the leading quotes force Netscape to create a string value) and calls the built-in function eval(), using the string as its parameter. Notice that the string built is going to be either "redness," "greenness," or "blueness"-one of the global variables you\'re using to hold the three color components. The eval() function returns the value contained in the specified variable. The color value is set to 1 plus that value (placing the 1 first forces Netscape to create an integer value).
minus() ONCLICK event handler function minus(text) The only difference between the plus() and minus() functions is that the minus() function adds -1 instead of 1 to the color value. So now let\'s see the setColor() function. setColor() function function setColor(colorName, colorValue) This time, a string is constructed that looks like what you\'d write to set the appropriate color variable. If this function is called with "red" and 100, for instance, a string "redness = 100" is created. The eval() function is called with this string as its argument and the new value is written to the appropriate variable. Then the functions setDec() and setHex() are called, with the name of the color as a parameter. The setDec() function updates the decimal TEXT field and the setHex() function updates the hexadecimal TEXT field. setDec() function function setDec(color) This is a very simple function. It creates a string setting the appropriate field from the appropriate variable and then uses the eval() function to execute the string. If called with a string of "green," for instance, the string that gets executed is "document.chooserForm.greenDec.value = greenness." The chooserForm part is the name of the FORM element that contains all of the fields in the page; the FORM tag is .
The setHex() function is a little more complicated. Strangely, while parseInt() knows how to read hexadecimal values, there is no built-in function to create a hexadecimal string. setHex() function function setHex(color) Again, a string is created that is then executed by eval(). This time, the actual value of the color variable has to be extracted and converted to a hexadecimal string. toHex2() function function toHex2(value) The function toHex2() creates a string consisting of two hexadecimal digits. The first digit is created by the function toHex() (seen in Listing 6.10), called with the value divided by 16. (Recall that using Math.floor() on the result of a divide gives the integer quotient.) The second digit is also created by the function toHex(), called with the value logically ANDed with 15. This is equivalent to using the modulus operator with a second operand of 16 (try it and see!). toHex() function function toHex(value) If the value passed to toHex() is less than or equal to 9, the value is returned as is. Otherwise, the values of 10 to 15 are "translated" to "A," "B," "C," "D," "E," and "F," respectively. If this function were to be used in a library, which is a good possibility, it would need to be a little more robust; hence the final return "??" + value + "??" which handles values that are out of range. So far, you have gotten data from the table\'s decimal, hexadecimal, plus, and minus input fields; set the appropriate variable; and updated the decimal and hexadecimal field values. It was relatively easy, and there wasn\'t a lot of code (discounting the 140 OPTION elements in the SELECT element). This particular technique, by the way, is an example of a powerful object-oriented paradigm called "model-view-controller," or MVC for short. The controller objects (the TEXT and BUTTON fields) update the model objects (the redness, greenness, and blueness variables); the model objects, in turn, update their views (the TEXT field values). Now let\'s tie in the SELECT field. readSelector() ONCHANGE event handler function readSelector(selector) The readSelector() function gets the index of the selected field from the select object\'s selectedIndex property. Clicking on an already selected OPTION field results in "unselection" of that OPTION. When that happens, the selectedIndex property is set to -1, indicating no selection. In that case, readSelector() simply returns. Otherwise, the selected option\'s value is retrieved (selector[ index ].value), appended to a "0x" string as in the readHex() function in Listing 6.3, and passed to parseInt(). The result is divided by 65536 (0x010000), yielding the red component. The remainder is obtained by ANDing with 65535 (0x00FFFF). The remaining value is divided by 256, yielding the green component, and ANDed with 255 (0x0000FF) to yield the blue component. The setColor() function is called for each color. As a result, when a named color is selected from the list, its red, green, and blue components show up in the text fields. The user can then fine-tune the named color, adding to and subtracting from the red, green, and blue components to his or her liking. Assigning a color to a attributeAlthough the top part of the page is devoted to selecting a color, the middle part is devoted to assigning the currently selected color to one of the five attributes. As with the color selection, let\'s use a table to neatly arrange the attributes.
Attribute table HTML As with the color selection discussed earlier, let\'s put the model-view-controller paradigm to work. The model consists of five global variables that contain the current color values for each of the attributes: var newBGCOLOR = "#FFFFFF"; These colors are arbitrary, by the way; it\'s a serviceable combination-white background, black text, blue links, red visited links, and green active links.
A selector for attribute selection Apply This Color To Which Attribute? Now you\'ll need some code to tie it all together. The attributeSelector() function, which you can see set as the SELECT field\'s ONCHANGE event handler is a good start. attributeSelector() ONCHANGE event handler function attributeSelector(selector)
setAttribute() function function setAttribute(attributeName) The setAttribute() function creates a color string consisting of a # character followed by the current values of the redness, greenness, and blueness variables. The values are run through toHex2() (see Listing 6.9) and a string is created to set the attribute variable and then executed. (In the case of the ALINK attribute, for example, the string would look like newALINK = "#rrggbb", where "rr," "gg," and "bb" are the current redness, greenness, and blueness values.) The attribute view in the table is then updated by creating a string and executing it. (In the case of the ALINK attribute, for example, the string would look like document.chooserForm.ALINK.value = "#rrggbb".) You need one more function to protect the field values from being overwritten. The fields are TEXT fields, because you can easily write to them using JavaScript code, as does setAttribute(). This also means that the user can place text in them. You need a function to restore the fields, such as restoreAttribute(). restoreAttribute() function function restoreAttribute(attribute) The restoreAttribute() function is set up as the ONCHANGE event handler for all five attribute input fields. The function creates and executes a string to restore the field\'s contents from the appropriate variable. The string created and executed for the BGCOLOR field, for example, would be document.chooserForm.BGCOLOR.value = newBGCOLOR Submit and ResetThe top part of the page selects the color. The middle part of the page assigns the selected color to an attribute. The bottom part of the page lets the user see the attribute combination or to reset the combination. The non-JavaScript solution used SUBMIT and RESET input fields. SUBMIT and RESET buttons (not!) The buttons have ONCLICK event handlers-resetViewScreen() and set- Defaults(). setDefaults() function function setDefaults() The setDefaults() function uses setColor() to initialize the redness, greenness, and blueness variables to 0; the setColor() calls will initialize the color selection fields. The attribute color variables are reset to the default colors and the attribute fields are set to the same colors. The following shows the rather busier resetViewScreen() function. resetViewScreen() function function resetViewScreen() The resetViewScreen() function draws the frame in the bottom of the window. It uses the functions drawLine() and drawLineBreak() to draw the frame.
function drawLine(s) Improving the solutionYou can improve upon the JavaScript solution in several ways. There are other ways to select colors, such as using percentage values for the red, green, and blue components. You could create a client-side image map and generate an RGB color based on where the user clicks on the map. The solution prevents the user from entering data into the attribute fields. Why not allow the user to enter a standard RGB color value in #rrggbb format? It would be useful to reload the color selection table from one of the attribute fields. For example, you might try out a color combination and decide that it would be better if you could adjust one of the attributes a little bit. It would be easier to be able to click on a button and reload the red, green, and blue values directly from the attribute instead of loading the values by hand. You could use cookies to save a color solution, and even let the user supply a name for the solution. When you brought up the color chooser the next time, you could reload the color table from one of the user\'s cookies. Modifying the example for your own useThis is not the kind of page that needs to be modified so that you can use it; it\'s a complete application in its own right, and it is not tied to anyone\'s URLs. Feel free to try some of the modifications suggested in the previous section. Modify the visible text and button names. Do not alter the field names, however; the code requires the field names to tie into the data they control. |