So far we have just been looking at what objects are, how to create them, and how to use them. Now, let's take a look at some of the more useful objects that are native to JavaScript, that is, those that JavaScript makes available for us to use.
We won't be looking at all of the native JavaScript objects, just some of the more commonly used ones, namely the String object, the Math object, the Array object, and the Date object. However, Appendix B gives a full listing of the JavaScript native objects. Later in the book, we'll also be dedicating a whole chapter to each of the more complex objects, such as the String object (Chapter 8) and the Date object (Chapter 9).
Like most objects, String objects need to be created before they can be used. To create a String object, we can write
var string1 = new String("Hello"); var string2 = new String(123); var string3 = new String(123.456);
However, as we have seen, we can also declare a string primitive and use it as if it were a String object, letting JavaScript do the conversion to an object for us behind the scenes. For example
var string1 = "Hello";
Using this technique is preferable so long as it's clear to JavaScript what object we expect to be created in the background. If the primitive data type is a string, this won't be a problem and JavaScript will work it out. The advantages to doing it this way are that there is no need to create a String object itself and we avoid the troubles with comparing string objects. When trying to compare them with primitive string values, the actual values are compared, but with String objects, it's the object references that are compared.
The String object has a vast number of methods and properties of which you can find full details in Appendix B. In this section, we'll only be looking at some of the less complex and more commonly used methods. However, in Chapter 8, we'll look at some of the trickier, but very powerful, methods associated with strings and the regular expression object (RegExp). Regular expressions provide a very powerful means of searching strings for patterns of characters. For example, if we want to find "Paul" where it exists as a whole word in the string "Pauline, Paul, Paula," we need to use regular expressions. However, they can be a little tricky to use, so we won't discuss them further in this chapter—we want to save some fun for later!
When using most of the String object's methods, it helps to remember that a string is just a series of individual characters and that each character has a position, or index, a little like arrays. Just like arrays, the first position, or index, is labeled 0 and not 1. So, for example, the string "Hello World" has the character positions shown in the following table.
Character Index |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
Character |
H |
e |
l |
l |
o |
W |
o |
r |
l |
d |
The length property simply returns the number of characters in the string. For example
var myName = new String("Paul"); document.write(myName.length);
will write the length of the string "Paul" (that is, 4) to the page.
If you want to find out information about a single character within a string, you need the charAt() and charCodeAt() methods. These methods can be very useful for checking the validity of user input, something we'll see more of in Chapter 6 when we look at HTML forms.
The charAt() method takes one parameter: the index position of the character you want in the string. It then returns that character. charAt() treats the positions of the string characters as starting at 0, so the first character is at index 0, the second at index 1, and so on.
For example, to find the last character in a string, we could use the code
var myString = prompt("Enter some text","Hello World!"); var theLastChar = myString.charAt(myString.length - 1); document.write("The last character is " + theLastChar);
In the first line we prompt the user for a string, with the default of "Hello World!" and store this string in the variable myString.
In the next line, we use the charAt() method to retrieve the last character in the string. We use the index position of (myString.length - 1). Why? Let's take the string "Hello World!" as an example. The length of this string is 12, but the last character position is 11 since the indexing starts at 0. Therefore, we need to subtract one from the length of the string to get the last character position.
In the final line, we write the last character in the string to the page.
The charCodeAt() method is similar in use to the charAt() method, but instead of returning the character itself, it returns a number that represents the decimal character code in the Unicode character set for that character. Recall that computers only understand numbers—to the computer, all our strings are just number data. When we request text rather than numbers, the computer does a conversion based on its internal understanding of each number and provides the respective character.
For example, to find the character code of the first character in a string, we could write
var myString = prompt("Enter some text","Hello World!"); var theFirstCharCode = myString.charCodeAt(0); document.write("The first character code is " + theFirstCharCode);
which will get the character code for the character at index position zero in the string given by the user, and write it out to the page.
Character codes go in order so, for example, the letter A has the code 65, B has 66, and so on. Lowercase letters start at 97 (a is 97, b is 98, and so on). Digits go from 48 (for the number 0) to 57 (for the number 9). You can use this information for various purposes, as we'll see in the next example.
The following is an example that detects the type of the character at the start of a given string; that is, whether the character is uppercase, lowercase, numeric, or other.
<html> <head> <script language="JavaScript" type="text/javascript"> function checkCharType(charToCheck) { var returnValue = "O"; var charCode = charToCheck.charCodeAt(0); if (charCode >= "A".charCodeAt(0) && charCode <= "Z".charCodeAt(0)) { returnValue = "U"; } else if (charCode >= "a".charCodeAt(0) && charCode <= "z".charCodeAt(0)) { returnValue = "L"; } else if (charCode >= "0".charCodeAt(0) && charCode <= "9".charCodeAt(0)) { returnValue = "N"; } return returnValue; } </script> <head> <body> <script language="JavaScript" type="text/javascript"> var myString = prompt("Enter some text","Hello World!"); switch (checkCharType(myString)) { case "U": document.write("First character was upper case"); break; case "L": document.write("First character was lower case"); break; case "N": document.write("First character was a number"); break; default: document.write("First character was not a character or a number"); } </script> </body> </html>
Type in the code and save it as ch4_examp1.htm.
When you load the page into your browser, you will be prompted for a string. A message will then be written to the page informing you of the type of the first character that you entered—whether it is uppercase, lowercase, a number, or something else, such as a punctuation mark.
To start with, we define a function checkCharType(), which is used in the body of the page. We start this function by declaring the variable returnValue and initializing it to the character "O" to indicate it's some other character than a lowercase letter, uppercase letter, or numerical character.
function checkCharType(charToCheck) { var returnValue = "O";
We use this variable as the value to be returned at the end of the function, indicating the type of character. It will take the values U for uppercase, L for lowercase, N for number, and O for other.
The next line in the function uses the charCodeAt() method to get the character code of the first character in the string stored in charToCheck, which is the function's only parameter. The character code is stored in the variable charCode.
var charCode = charToCheck.charCodeAt(0);
In the following lines we have a series of if statements, which check what range of values the character code falls within. We know that if it falls between the character codes for A and Z, it's uppercase, and so we assign the variable returnValue the value U. If the character code falls between the character codes for a and z, it's lowercase, and so we assign the value L to the variable returnValue. If the character code falls between the character codes for 0 and 9, it's a number, and we assign the value N to the variable returnValue. If the value falls into none of these ranges, then the variable retains its initialization value of O for other, and we don't have to do anything.
if (charCode >= "A".charCodeAt(0) && charCode <= "Z".charCodeAt(0)) { returnValue = "U"; } else if (charCode >= "a".charCodeAt(0) && charCode <= "z".charCodeAt(0)) { returnValue = "L"; } else if (charCode >= "0".charCodeAt(0) && charCode <= "9".charCodeAt(0)) { returnValue = "N"; }
This probably seems a bit weird at first, so let's see what JavaScript is doing with our code. When we write
"A".charCodeAt(0)
it appears that we are trying to use a method of the String object on a string literal, which is the same as primitive string in that it's just characters and not an object. However, JavaScript realizes what we are doing and does the necessary conversion of literal character "A" into a temporary String object containing "A". Then, and only then, JavaScript performs the charCodeAt() method on the String object it has created in the background. When it has finished, the String object is disposed of. Basically, this a shorthand way of writing
var myChar = new String("A"); myChar.charCodeAt(0);
In either case the first (and in this string the only) character's code is returned to us. For example, "A".charCodeAt(0) will return the number 65.
Finally we come to the end of the function and return the returnValue variable to where the function was called.
return returnValue; }
You might wonder why we bother using the variable returnValue at all, instead of just returning its value. For example, we could write the code as follows:
if (charCode >= "A".charCodeAt(0) && charCode <= "Z".charCodeAt(0)) { return "U"; } else if (charCode >= "a".charCodeAt(0) && charCode <= "z".charCodeAt(0)) { return "L"; } else if (charCode >= "0".charCodeAt(0) && charCode <= "9".charCodeAt(0)) { return "N"; } return "O";
This would work fine, so why not do it this way? The disadvantage of this way is that it's difficult to follow the flow of execution of the function, which is not that bad in a small function like this, but can get tricky in bigger functions. With the original code we always know exactly where the function execution stops: It stops at the end with the only return statement. The version of the function just shown finishes when any of the return statements is reached, so there are four possible places where the function might end.
In the body of our page, we have some test code to check that the function works. We first use the variable myString, initialized to "Hello World!" or whatever the user enters into the prompt box, as our test string.
var myString = prompt("Enter some text","Hello World!");
Next, the switch statement uses the checkCharType() function that we defined earlier in its comparison expression. Depending on what is returned by the function, one of the case statements will execute and let the user know what the character type was.
switch (checkCharType(myString)) { case "U": document.write("First character was upper case"); break; case "L": document.write("First character was lower case"); break; case "N": document.write("First character was a number"); break; default: document.write("First character was not a character or a number"); }
That completes the example, but before we move on, it's worth noting that this example is just that, an example of using charCodeAt(). In practice, it would be much easier to just write
if (char >= "A" && char <= "Z")
rather than
if (charCode >= "A".charCodeAt(0) && charCode <= "Z".charCodeAt(0))
which we have used here.
The method fromCharCode() can be thought of as the opposite to charCodeAt(), in that you pass it a series of comma-separated numbers representing character codes, and it converts them to a single string.
However, the fromCharCode() method is unusual in that it's a static method—we don't need to have created a String object to use it with. Instead, we can use the String expression.
For example, the following lines put the string "ABC" into the variable myString.
var myString; myString = String.fromCharCode(65,66,67);
The fromCharCode() method can be very useful when used with variables. For example, to build up a string consisting of all the uppercase letters of the alphabet, we could use the following code:
var myString = ""; var charCode; for (charCode = 65; charCode <= 90; charCode++) { myString = myString + String.fromCharCode(charCode); } document.write(myString);
We use the for loop to select each character from A to Z in turn and concatenate this to myString. Note that, while this is fine as an example, it is more efficient and less memory-hungry to simply write
var myString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
The methods indexOf() and lastIndexOf() are used for searching for the occurrence of one string inside another. A string contained inside another is usually termed a substring. They are useful when we have a string of information, but only want a small part of it. For example, in our trivia quiz when someone enters a text answer, we want to check if certain keywords are present within the string.
Both indexOf() and lastIndexOf() take two parameters:
The string you want to find
The character position you want to start searching from (optional)
As with the charAt() method, character positions start at zero. If you don't include the second parameter, searching starts from the beginning of the string.
The return value of indexOf() and lastIndexOf() is the character position in the string at which the substring was found. Again, it's zero-based, so if the substring is found at the start of the string, then 0 is returned. If there is no match, then the value -1 is returned.
For example, to search for the substring "Paul" in the string "Hello paul. How are you Paul", we may use the code
<script language="JavaScript" type="text/javascript"> var myString = "Hello paul. How are you Paul"; var foundAtPosition; foundAtPosition = myString.indexOf("Paul"); alert(foundAtPosition); </script>
This code should result in a message box containing the number 24, which is the character position of "Paul". You might be wondering why it's 24, which clearly refers to the second "Paul" in the string, rather than 6 for the first "paul". Well, this is due to case sensitivity again. It's laboring the point a bit, but JavaScript takes case sensitivity very seriously, both in its syntax and when making comparisons. If you type IndexOf() instead of indexOf(), JavaScript will complain. Similarly, "paul" is not the same as "Paul". Mistakes with case are so easy to make, even for experts, that it's best to be very aware of case when programming.
We've seen indexOf() in action, but how does lastIndexOf() differ? Well, whereas indexOf() starts searching from the beginning of the string, or the position you specified in the second parameter, and works towards the end, lastIndexOf() starts at the end of the string, or the position you specified, and works towards the beginning of the string.
In the example we first search using indexOf(), which finds the first "Paul" (changed to the correct case from the last example). The alert box displays this result, which is character position 6. Then we search using lastIndexOf(). This starts searching at the end of the string, and so the first Paul it comes to is the last one in the string at character position 24. Therefore, the second alert box displays the result 24.
<script language="JavaScript" type="text/javascript"> var myString = "Hello Paul. How are you Paul"; var foundAtPosition; foundAtPosition = myString.indexOf("Paul"); alert(foundAtPosition); foundAtPosition = myString.lastIndexOf("Paul"); alert(foundAtPosition); </script>
In this example, let's look at how we can use the start character position parameter of indexOf(). Here, we will count how many times the word Wrox appears in the string.
<html> <body> <script language="JavaScript" type="text/javascript"> var myString = "Welcome to Wrox books. "; myString = myString + "The Wrox website is www.wrox.com. "; myString = myString + "Visit the Wrox website today. Thanks for buying Wrox"; var foundAtPosition = 0; var wroxCount = 0; while ( foundAtPosition != -1) { foundAtPosition = myString.indexOf("Wrox",foundAtPosition); if (foundAtPosition != -1) { wroxCount++; foundAtPosition++; } } document.write("There are " + wroxCount + " occurrences of the word Wrox"); </script> </body> </html>
Save this example as ch4_examp2.htm. When you load the page into your browser, you should see the sentence: There are four occurrences of the word Wrox.
At the top of the script block, we have built up a string inside the variable myString, which we then want to search for the occurrence of the word Wrox. We also define two variables: wroxCount will contain the number of times Wrox is found in the string, and foundAtPosition will contain the position in the string of the current occurrence of the substring Wrox.
We have then used a while loop, which continues looping all the while we are finding the word Wrox in the string, that is, while the variable foundAtPosition is not equal to -1. Inside the while loop, we have the line
foundAtPosition = myString.indexOf("Wrox",foundAtPosition);
Here we search for the next occurrence of the substring Wrox in the string myString. How do we make sure that we get the next occurrence? We use the variable foundAtPosition to give us the starting position of our search, because this contains the index after the index position of the last occurrence of the substring Wrox. We assign the variable foundAtPosition to the result of our search, the index position of the next occurrence of the substring Wrox.
Each time Wrox is found (that is, foundAtPosition is not -1) we increase the variable wroxCount, which counts how many times we have found the substring, and we increase foundAtPosition so that we continue the search at the next position in the string.
if (foundAtPosition != -1) { wroxCount++; foundAtPosition++; }
Finally, we document.write() the value of the variable wroxCount to the page.
In the previous chapter we talked about the danger of infinite loops, and you can see that there is a danger of one here. If the foundAtPosition++ were removed, we'd keep searching from the same starting point and never move to find the next occurrence of the word Wrox.
The indexOf() and lastIndexOf() methods are more useful when coupled with the substr() and substring() methods, which we'll be looking at in the next section. Using a combination of these methods allows us to cut substrings out of a string.
If we wanted to cut out part of a string and assign that cut out part to another variable or use it in an expression, we would use the substr() and substring() methods. Both methods provide the same end result, that is, a part of a string, but they differ in the parameters they require.
The method substring() takes two parameters: the character start position and the character end position of the part of the string we want. The second parameter is optional; if you don't include it, all characters from the start position to the end of the string are included.
For example, if our string is "JavaScript" and we want just the text "Java", we could call the method like so:
var myString = "JavaScript"; var mySubString = myString.substring(0,4); alert(mySubString);
As with all the methods of the String object so far, the character positions start at zero. However, you might be wondering why we specified the end character as 4. This method is a little confusing because the end character is the end marker; it's not included in the substring that is cut out. It helps to think of the parameters as specifying the length of the string being returned: the parameters 0 and 4 will return (4 - 0) characters starting at and including the character at position 0. Depicted graphically it looks like this:
Character Position |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Character |
J |
a |
v |
a |
S |
c |
r |
i |
p |
t |
Like substring(), the method substr() again takes two parameters, the first being the start position of the first character you want included in your substring. However, this time the second parameter specifies the length of the string of characters that you want to cut out of the longer string. For example, we could rewrite the preceding code as
var myString = "JavaScript"; var mySubString = myString.substr(0,4); alert(mySubString);
As with the substring() method, the second parameter is optional. If you don't include it, all the characters from the start position onward will be included.
The main reason for using one method rather than the other is that the substring() method is supported by IE 3+ and by NN 2+ browsers. However, the substr() method only works with version 4 (and later) browsers.
Let's look at the use of the substr() and lastIndexOf() methods together. In the next chapter, we'll see how we can retrieve the file path and name of the currently loaded web page. However, there is no way of just retrieving the file name alone. So if, for example, your file is http://mywebsite/temp/myfile.htm, you may need to extract the myfile.htm part. This is where substr() and lastIndexOf() are useful.
var fileName = window.location.href; fileName = fileName.substr(fileName.lastIndexOf("\") + 1); document.write("The file name of this page is " + fileName);
The first line sets the variable fileName to the current file path and name, such as /mywebsite/temp/myfile.htm. Don't worry about understanding this line; we'll be looking at it in the next chapter.
The second line is where the interesting action is. You can see that I've used the return value of the lastIndexOf() method as a parameter for another method, something that's perfectly correct and very useful. Our goal in using fileName.lastIndexOf("/") is to find the position of the final forward slash (/), which will be the last character before the name of the file. We add one to this value, because we don't want to include that character, and then pass this new value to the substr() method. There's no second parameter here (the length), because we don't know it. As a result, substr() will return all the characters right to the end of the string, which is what we want.
If we want to change the case of a string, for example to remove case sensitivity when comparing strings, we need the toLowerCase() and toUpperCase() methods. It's not hard to guess what these two methods do. Both of them return a string that is the value of the string in the String object, but with its case converted to either upper or lower depending on the method invoked. Any non-alphabetical characters remain unchanged by these functions.
In the following example, we can see that by changing the case of both strings we can compare them without case sensitivity being an issue.
var myString = "I Don't Care About Case" if (myString.toLowerCase() == "i don't care about case") { alert("Who cares about case?"); }
Even though toLowerCase() and toUpperCase() don't take any parameters, you must remember to put the two empty parentheses at the end, that is, (), if you want to call a method.
The Math object provides a number of useful mathematical functions and number manipulation methods. We'll be taking a look at some of them here, but you'll find the rest described in Appendix B.
The Math object is a little unusual in that JavaScript automatically creates it for you. There's no need to declare a variable as a Math object or define a new Math object before being able to use it, making it a little bit easier to use.
The properties of the Math object include some useful math constants, such as the PI property (giving the value 3.14159 and so on). We access these properties, as usual, by placing a dot after the object name (Math) and then writing the property name. For example, to calculate the area of a circle, we may use the following code:
var radius = prompt("Give the radius of the circle", ""); var area = (Math.PI)*radius*radius; document.write("The area is " + area);
The methods of the Math object include some operations that are impossible, or complex, to perform using the standard mathematical operators (+, –, *, and /). For example, the cos() method returns the cosine of the value passed as a parameter. We'll look at a few of these methods now.
The abs() method returns the absolute value of the number passed as its parameter. Essentially, this means that it returns the positive value of the number. So -1 is returned as 1, -4 as 4, and so on. However, 1 would be returned as 1 because it's already positive.
For example, the following code would write the number 101 to the page.
var myNumber = -101; document.write(Math.abs(myNumber));
The ceil() method always rounds a number up to the next largest whole number or integer. So 10.01 becomes 11, and –9.99 becomes –9 (because –9 is greater than –10). The ceil() method has just one parameter, namely the number you want rounded up.
Using ceil() is different from using the parseInt() function we saw in Chapter 2, because parseInt() simply chops off any numbers after the decimal point to leave a whole number, whereas ceil() rounds the number up.
For example, the following code writes two lines in the page, the first containing the number 102 and the second containing the number 101.
var myNumber = 101.01; document.write(Math.ceil(myNumber) + "<br>"); document.write(parseInt(myNumber));
Like the ceil() method, the floor() method removes any numbers after the decimal point, and returns a whole number or integer. The difference is that floor() always rounds the number down. So if we pass 10.01 we would be returned 10, and if we pass –9.99, we will see –10 returned.
The round() method is very similar to ceil() and floor(), except that instead of always rounding up or always rounding down, it rounds up only if the decimal part is .5 or greater, and rounds down otherwise.
For example
var myNumber = 44.5; document.write(Math.round(myNumber) + "<br>"); myNumber = 44.49; document.write(Math.round(myNumber));
would write the numbers 45 and 44 to the page.
As we have seen, the ceil(), floor(), and round() methods all remove the numbers after a decimal point and return just a whole number. However, which whole number they return depends on the method used: floor() returns the smallest, ceil() the highest, and round() the nearest equivalent integer. This can be a little confusing, so the following is a table of values and what whole number would be returned if these values were passed to the parseInt() function, and ceil(), floor(), and round() methods.
Parameter |
parseInt() returns |
ceil() returns |
floor() returns |
round() returns |
---|---|---|---|---|
10.25 |
10 |
11 |
10 |
10 |
10.75 |
10 |
11 |
10 |
11 |
10.5 |
10 |
11 |
10 |
11 |
–10.25 |
–10 |
–10 |
–11 |
–10 |
–10.75 |
–10 |
–10 |
–11 |
–11 |
–10.5 |
–10 |
–10 |
–11 |
–10 |
Note |
Remember that parseInt() is a native JavaScript function and not a method of the Math object, like the other methods presented in this table. |
If you're still not sure about rounding numbers, the following example should help. Here, we'll look at a calculator that gets a number from the user, then writes out what the result would be when we pass that number to parseInt(), ceil(), floor(), and round().
<html> <body> <script language="JavaScript" type="text/javascript"> var myNumber = prompt("Enter the number to be rounded",""); document.write("<h3>The number you entered was " + myNumber + "</h3><br>"); document.write("<p>The rounding results for this number are</p>"); document.write("<table width=150 border=1>"); document.write("<tr><th>Method</th><th>Result</th></tr>"); document.write("<tr><td>parseInt()</td><td>"+ parseInt(myNumber) +"</td></tr>"); document.write("<tr><td>ceil()</td><td>" + Math.ceil(myNumber) + "</td></tr>"); document.write("<tr><td>floor()</td><td>"+ Math.floor(myNumber) + "</td></tr>"); document.write("<tr><td>round()</td><td>" + Math.round(myNumber) +"</td></tr>"); document.write("</table>") </script> </body> </html>
Save this as ch4_examp3.htm and load it into a web browser. In the prompt box, enter a number, for example -12.354, and click OK. The results of this number being passed to parseInt(), ceil(), floor(), and round() will be displayed in the page formatted inside a table as shown in Figure 4-1.
Our first task is to get the number to be rounded from the user:
var myNumber = prompt("Enter the number to be rounded","");
Then, we write out the number and some descriptive text.
document.write("<h3>The number you entered was " + myNumber + "</h3><br>"); document.write("<p>The rounding results for this number are</p>");
Notice how this time some HTML tags for formatting have been included—the main header being in <h3> tags and the description of what the table means being inside a paragraph <p> tag.
Next we create our table of results.
document.write("<table width=150 border=1>"); document.write("<tr><th>Method</th><th>Result</th></tr>"); document.write("<tr><td>parseInt()</td><td>"+ parseInt(myNumber) +"</td></tr>"); document.write("<tr><td>ceil()</td><td>" + Math.ceil(myNumber) + "</td></tr>"); document.write("<tr><td>floor()</td><td>"+ Math.floor(myNumber) + "</td></tr>"); document.write("<tr><td>round()</td><td>" + Math.round(myNumber) +"</td></tr>"); document.write("</table>")
We create the table header first before actually displaying the results of each rounding function on a separate row. You can see how easy it is to dynamically create HTML inside the web page using just JavaScript. The principles are the same as with HTML in a page: We must make sure our tag's syntax is valid or otherwise things will appear strange or not appear at all.
Each row follows the same principle, just using a different rounding function. Let's look at the first row, which displays the results of parseInt().
Inside the string to be written out to the page, we start by creating the table row with the <tr> tag. Then we create a table cell with a <td> tag and insert the name of the method from which the results are being displayed on this row. Then we close the cell with </td> and open a new one with <td>. Inside this next cell we are placing the actual results of the parseInt() function. Although a number is returned by parseInt(), because we are concatenating it to a string, JavaScript automatically converts the number returned by parseInt() into a string before concatenating. All this happens in the background without us needing to do a thing. Finally, we close the cell and the row with </td></tr>.
The random() method returns a random floating-point number in the range between 0 and 1, where 0 is included and 1 is not. This can be very useful for displaying random banner images or for writing a JavaScript game.
Let's look at how you would mimic the roll of a single die. In the following page, ten random numbers are written to the page. Click the browser's Refresh button to get another set of random numbers.
<html> <body> <script language="JavaScript" type="text/javascript"> var throwCount; var diceThrow; for (throwCount = 0; throwCount < 10; throwCount++) { diceThrow = (Math.floor(Math.random() * 6) + 1); document.write(diceThrow + "<br>"); } </script> </body> </html>
We want diceThrow to be between 1 and 6. The random() function returns a floating-point number between 0 and just under 1. By multiplying this number by 6, we get a number between 0 and just under 6. Then by adding 1, we get a number between 1 and just under 7. By using floor() to always round it down to the next lowest whole number, we can ensure that we'll end up with a number between 1 and 6.
If we wanted a random number between 1 and 100, we would just change the code so that Math.random() is multiplied by 100 rather than 6.
The pow() method raises a number to a specified power. It takes two parameters, the first being the number you want raised to a power, and the second being the power itself. For example, to raise 2 to the power of 8 (that is, to calculate 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2), we would write Math.pow(2,8)—the result being 256. Unlike some of the other mathematical methods, like sin(), cos(), and acos(), which are not commonly used in web programming unless it's a scientific application you're writing, the pow() method can often prove very useful.
In the following example, we write a function using pow(), which fixes the number of decimal places in a number—a function that's missing from earlier versions of JavaScript though now added to Jscript 5.5 and JavaScript 1.5, as we'll see later in this chapter. This helps demonstrate that even where a function is missing from JavaScript, you can usually use existing functions to create what you want.
<html> <head> <script language="JavaScript" type="text/javascript"> function fix(fixNumber, decimalPlaces) { var div = Math.pow(10,decimalPlaces); fixNumber = Math.round(fixNumber * div) / div; return fixNumber; } </script> </head> <body> <script language="JavaScript" type="text/javascript"> var number1 = prompt("Enter the number with decimal places you want to fix",""); var number2 = prompt("How many decimal places do you want?",""); document.write(number1 + " fixed to " + number2 + " decimal places is: "); document.write(fix(number1,number2)); </script> </body> </html>
Save the page as ch4_examp4.htm. When you load the page into your browser, you will be presented with two prompt boxes. In the first, enter the number for which you want to fix the number of decimal places, for example 2.2345. In the second, enter the number of decimal places you want fixed, for example 2. Then the result of fixing the number you have entered to the number of decimal places you have chosen will be written to the page as shown in Figure 4-2. For the example numbers I have given, this will be 2.23.
In the head of the page we define the function fix(). This function will fix its fixNumber parameter to a maximum of its decimalPlaces parameter's number of digits after the decimal place. For example, fixing 34.76459 to a maximum of 3 decimal places will return 34.765.
The first line of code in the function sets the variable div to the number 10 raised to the power of the number of decimal places we want.
function fix(fixNumber, decimalPlaces) { var div = Math.pow(10,decimalPlaces);
Then, in the next line, we calculate the new number.
fixNumber = Math.round(fixNumber * div) / div;
What the code Math.round(fixNumber * div) does is move the decimal point in the number that we are converting to after the point in the number that we want to keep. So for 2.2345, if we want to keep two decimal places, we convert it to 223.45. The Math.round() method rounds this number to the nearest integer (in this case 223) and so removes any undesired decimal part.
We then convert this number back into the fraction it should be, but of course only the fractional part we want is left. We do this by dividing by the same number (div) that we multiplied by. In our example, we divide 223 by 100, which leaves 2.23. This is 2.2345 fixed to two decimal places. This value is returned to the calling code in the line
return fixNumber; }
In the body of the page we use two prompt boxes to get numbers from the user. We then display the results of using these numbers in our fix() function to the user using document.write().
As with the String object, Number objects need to be created before they can be used. To create a Number object, we can write
var firstNumber = new Number(123); var secondNumber = new Number('123');
However, as we have seen, we can also declare a number as primitive and use it as if it were a Number object, letting JavaScript do the conversion to an object for us behind the scenes. For example
var myNumber = 123.765;
As with the String object, this technique is preferable so long as it's clear to JavaScript what object we expect to be created in the background. So for example
var myNumber = "123.567";
will lead JavaScript to assume, quite rightly, that it's a string and any attempts to use the Number object's methods will fail.
We'll look at just the toFixed() method of the Number object because that's the most useful method for everyday use.
The toFixed() method is new to JavaScript 1.5 and Jscript 5.5—so basically it's available in Netscape 6+ and IE 5.5+ only. The method cuts a number off after a certain point. Let's say we wanted to display a price after sales tax. If our price is $9.99 and sales tax is 7.5%, that means the after-tax cost will be $10.73925. Well, this is rather an odd amount for a money transaction—what we really want to do is fix the number to no more than two decimal places. Let's create an example.
var itemCost = 9.99; var itemCostAfterTax = 9.99 * 1.075; document.write("Item cost is $" + itemCostAfterTax + "<br>"); itemCostAfterTax = itemCostAfterTax.toFixed(2); document.write("Item cost fixed to 2 decimal places is " + itemCostAfterTax);
The first document.write() will output the following to the page:
Item cost is $10.73925
However, this is not the format we want; instead we want two decimal places, so on the next line, we enter
itemCostAfterTax = itemCostAfterTax.toFixed(2);
We use the toFixed() method of the Number object to fix the number variable that itemCostAfterTax holds to two decimal places. The method's only parameter is the number of decimal places we want our number fixed to. This line means that the next document.write displays
Item cost fixed to 2 decimal places is $10.74
The first thing you might wonder is why 10.74 and not 10.73? Well, the toFixed() method doesn't just chop off the digits not required; it also rounds up or down. In this case, it was 10.739, which rounds up to 10.74. If it'd been 10.732, it would have been rounded down to 10.73.
Note that we can only fix a number from 0 to 20 decimal places.
Because the method is only supported on newer browsers, it's a good idea to double-check first if the browser supports it, like so:
var varNumber = 22.234; if (varNumber.toFixed) { // Browser supports toFixed() method varNumber = varNumber.toFixed(2) } else { // Browser doesnt support toFixed() method so use some other code var div = Math.pow(10,2); varNumber = Math.round(varNumber * div) / div; }
We saw how to create and use arrays in Chapter 2, and earlier in this chapter I admitted to you that they are actually objects.
As well as storing data, Array objects also provide a number of useful properties and methods we can use to manipulate the data in the array and find out information such as the size of the array.
Again, this is not an exhaustive look at every property and method of Array objects, but rather just some of the more useful ones. You'll find details of the rest in Appendix B.
The length property gives us the number of elements within an array. We have already seen this in use in the Trivia Quiz in Chapter 3. Sometimes we know exactly how long the array is, but there are situations where we may have been adding new elements to an array with no easy way of keeping track of how many have been added.
The length property can be used to find the index of the last element in the array. This is illustrated in the following example.
var names = new Array(); names[0] = "Paul"; names[1] = "Catherine"; names[11] = "Steve"; document.write("The last name is " + names[names.length - 1]);
Note that we have inserted data in the elements with index positions 0, 1, and 11. The array index starts at 0, so the last element is at index length - 1, which is 11, rather than the value of the length property, which is 12.
Another situation in which the length property proves useful is where a JavaScript method returns an array it has built itself. For example, in Chapter 8 on advanced string handling, we'll see that the String object has the split() method, which splits text into pieces and passes back the result as an Array object. Because JavaScript created the array, there is no way for us to know, without the length property, what the index is of the last element in the array.
If we want to take two separate arrays and join them together into one big array, we can use the Array object's concat() method. The concat() method returns a new array, which is the combination of the two arrays: the elements of the first array, then the elements of the second array. To do this, we use the method on our first array and pass the name of the second array as its parameter.
For example, say we have two arrays, names and ages, and separately they look like this:
names array |
||||
---|---|---|---|---|
Element Index |
0 |
1 |
2 | |
Value |
Paul |
Catherine |
Steve |
ages array |
||||
---|---|---|---|---|
Element Index |
0 |
1 |
2 | |
Value |
31 |
29 |
34 |
If we combine them using names.concat(ages), we will get an array like this:
Element Index |
0 |
1 |
2 |
3 |
4 |
5 |
Value |
Paul |
Catherine |
Steve |
31 |
29 |
34 |
In the following code, this is exactly what we are doing.
var names = new Array("Paul","Catherine","Steve"); var ages = new Array(31,29,34); var concatArray; concatArray = names.concat(ages);
It's also possible to combine two arrays into one, but assign the new array to the name of the existing first array, using names = names.concat(ages).
If we were to use ages.concat(names), what would be the difference? Well, as you can see in the following table, the difference is that now the ages array elements are first, and the elements from the names array are concatenated on the end.
Element Index |
0 |
1 |
2 |
3 |
4 |
5 |
Value |
31 |
29 |
34 |
Paul |
Catherine |
Steve |
When using the concat() method it is important to note that it is only available in version 4 or later browsers.
When we just want to copy a portion of an array, we can use the slice() method. Using the slice() method, we can slice out a portion of the array and assign that to a new variable name. The slice() method has two parameters:
The index of the first element you want to be copied
The index of the element marking the end of the portion you are slicing out (optional)
Just like string copying with substr() and substring(), the start point is included in the copy, but the end point is not. Again, if you don't include the second parameter, all elements from the start index onward are copied.
Suppose we have the array names shown in the following table.
Index |
0 |
1 |
2 |
3 |
4 |
Value |
Paul |
Sarah |
Louise |
Adam |
Bob |
If we want to create a new array with elements 1, Sarah, and 2, Louise, we would specify a start index of 1 and an end index of 3. The code would look something like this:
var names = new Array("Paul","Sarah","Louise","Adam","Bob"); var slicedArray = names.slice(1,3);
Note that when JavaScript copies the array, it copies the new elements to an array where they have indexes 0 and 1, and not their old indexes of 1 and 2. After slicing, the slicedArray looks like this:
Index |
0 |
1 | |
Value |
Sarah |
Louise |
The first array, names, is unaffected by the slicing.
As with the concat() method, the slice() method is available only in version 4 and later browsers.
The join() method concatenates all the elements in an array and returns them as a string. It also allows you to specify any characters you want to insert between each element as they are joined together. The method has only one parameter, and that's the string we want between each element.
Things will be clearer if we look at an example. Let's imagine I have my weekly shopping list stored in an array, which looks something like this:
Index |
0 |
1 |
2 |
3 |
4 |
Value |
Eggs |
Milk |
Potatoes |
Cereal |
Banana |
Now I want to write out my shopping list to the page using document.write(). I want each item to be on a different line, so this means I need to use the <br> tag between each element. First, I need to declare my array.
var myShopping = new Array("Eggs","Milk","Potatoes","Cereal","Banana");
Now, let's convert the array into one string with the join() method.
var myShoppingList = myShopping.join("<br>");
Now the variable myShoppingList will hold the following text:
"Eggs<br>Milk<br>Potatoes<br>Cereal<br>Banana"
which we can write out to the page with document.write().
document.write(myShoppingList);
Now the shopping list will appear in the page with each item on a new line as shown in Figure 4-3.
If you have an array that contains similar data, such as a list of names or a list of ages, you may want to put them in alphabetical or numerical order. This is something that the sort() method makes very easy. In the following code, we define our array and then put it in ascending alphabetical order using names.sort(). Finally, we output it so that we can see that it's in order.
var names = new Array("Paul","Sarah","Louise","Adam","Bob"); var elementIndex; names.sort(); document.write("Now the names again in order" + "<br>"); for (elementIndex = 0; elementIndex < names.length; elementIndex++) { document.write(names[elementIndex] + "<br>"); }
Don't forget that the sorting is case sensitive, so Paul will come before paul. Remember that JavaScript stores letters encoded in their equivalent Unicode number. Sorting is done based on the Unicode number rather than the actual letter. It just happens that Unicode numbers match the order in the alphabet. However, lowercase letters are given a different sequence of numbers, which come after the uppercase letters. So the array with elements Adam, adam, Zoл, zoл, will be sorted to the order Adam, Zoл, adam, zoл.
Note that in our for statement we've used the Array object's length property in the condition statement, rather than inserting the length of the array (5) ourselves, like this:
for (elementIndex = 0; elementIndex < 5; elementIndex++)
Why do this? After all, we know in advance that there are five elements in the array. Well, what would happen if we altered the number of elements in our array by adding two more names?
var names = new Array("Paul","Sarah","Louise","Adam","Bob","Karen","Steve");
If we had inserted 5 rather than names.length, our loop code wouldn't work as we want it to. It wouldn't display the last two elements unless we changed the condition part of the for loop to 7. By using the length property, we've made life easier for ourselves because now there is no need to change code elsewhere if we add array elements.
OK, we've put things in ascending order, but what if we wanted descending order? That is where the reverse() method comes in.
The final method we'll look at for the Array object is the reverse() method, which—no prizes for guessing—reverses the order of the array so that the elements at the back are moved to the front. Let's take our shopping list again as an example.
Index |
0 |
1 |
2 |
3 |
4 |
Value |
Eggs |
Milk |
Potatoes |
Cereal |
Banana |
If we used the reverse() method
var myShopping = new Array("Eggs","Milk","Potatoes","Cereal","Banana"); myShopping.reverse();
we end up with the array elements in this order:
Index |
0 |
1 |
2 |
3 |
4 |
Value |
Banana |
Cereal |
Potatoes |
Milk |
Eggs |
To prove this we could write it to the page with the join() method we saw earlier.
var myShoppingList = myShopping.join("<br>") document.write(myShoppingList);
When used in conjunction with the sort() method, the reverse() method can be used to sort an array so that its elements appear in reverse alphabetical or numerical order. This is shown in the following example.
<html> <body> <script language="JavaScript" type="text/javascript"> var myShopping = new Array("Eggs","Milk","Potatoes","Cereal","Banana"); var ord = prompt("Enter 1 for alphabetical order, and -1 for reverse order", 1); if (ord == 1) { myShopping.sort(); document.write(myShopping.join("<br>")); } else if (ord == -1) { myShopping.sort(); myShopping.reverse(); document.write(myShopping.join("<br>")); } else { document.write("That is not a valid input"); } </script> </body> </html>
Save the example as ch4_examp5.htm. When you load this into your browser, you will be asked to enter some input depending on whether you want the array to be ordered in forward or backward order. If you enter 1, the array will be displayed in forward order. If you enter –1, the array will be displayed in reverse order. If you enter neither of these values, you will be told that your input was invalid.
At the top of the script block we define the array containing our shopping list. Next we define the variable ord to be the value entered by the user in a prompt box.
var ord = prompt("Enter 1 for alphabetical order, and -1 for reverse order", 1);
This value is used in the conditions of the if statements that follow. The first if checks whether the value of ord is 1, that is, whether the user wanted the array in alphabetical order. If so the following code is executed:
myShopping.sort(); document.write(myShopping.join("<br>"));
The array is sorted, and then displayed to the user on separate lines using the join() method. Next, in the else if statement we check whether the value of ord is -1, that is, whether the user wants the array in reverse alphabetical order. If so, the following code is executed:
myShopping.sort(); myShopping.reverse(); document.write(myShopping.join("<br>"));
Here, we sort the array before reversing its order. Again the array is displayed to the user using the join() method.
Finally, if ord has neither the value 1 nor the value -1, we tell the user that his input was invalid.
document.write("That is not a valid input");
The Date object handles everything to do with date and time in JavaScript. Using it, we can find out the date and time now, store our own dates and times, do calculations with these dates, and convert the dates into strings.
The Date object has a lot of methods and can be a little tricky to use, which is why Chapter 9 is dedicated to the date, time, and timers in JavaScript. We'll also see in Chapter 11 how we can use dates to determine if there's been anything new added to the website since the user last visited it. However, in this section, we'll focus on how to create a Date object and some of its more commonly used methods.
We can declare and initialize a Date object in four ways. In the first method, we just declare a new Date object without initializing its value. In this case, the date and time value will be set to the current date and time on the PC on which the script is run.
var theDate1 = new Date();
Secondly, we can define a Date object by passing the number of milliseconds since January 1, 1970 at 00:00:00 GMT. In the following example, the date is 31 January 2000 00:20:00 GMT (that is, 20 minutes past midnight).
var theDate2 = new Date(949278000000);
It's unlikely that you'll be using this way of defining a Date object very often, but this is how JavaScript actually stores the dates. The other formats for giving a date are simply for our convenience.
Next, we can pass a string representing a date, or a date and time. In the following example, we have "31 January 2000".
var theDate3 = new Date("31 January 2000");
However, we could have written 31 Jan 2000, Jan 31 2000, 01-31-2000, or any of a number of valid variations you'd commonly expect when writing down a date normally—if in doubt, try it out. Note that Netscape browsers don't support the string "01-31-2000" as a valid date format. If you are writing your web pages for an international audience outside of the U.S.A., you need to be aware of the different way of specifying dates. In the U.K. and many other places, the standard is day, month, year, whereas in the U.S., the standard is month, day, year. This can cause problems if you specify only numbers—JavaScript may think you're referring to a day when you meant a month. The easiest way to avoid such headaches is to, where possible, always use the name of the month. That way there can be no confusion.
In the fourth and final way, we initialize the Date object by passing the following parameters separated by commas: year, month, day, hours, minutes, seconds, and milliseconds. For example
var theDate4 = new Date(2000,0,31,15,35,20,20);
This date is actually 31 January 2000 at 15:35:20 and 20 milliseconds. You can specify just the date part if you wish and ignore the time.
Something to be aware of is that in this instance January is month 0, not month 1, as you'd expect, and December is month 11. It's a very easy mistake to make.
It's all very nice having stored a date, but how do we get the information out again? Well, we just use the get methods. These are summarized in the following table.
Method |
Returns |
---|---|
getDate() |
The day of the month. |
getDay() |
The day of the week as an integer, with Sunday as 0, Monday as 1, and so on. |
getMonth() |
The month as an integer, with January as 0, February as 1, and so on. |
getFullYear() |
The year as a four-digit number. |
toDateString() |
Returns the full date based on the current time zone as a human-readable string. For example "Wed 31 Dec 2003". Note this is available only in IE5.5+ and Netscape 6+. |
For example, if we want to get the month in ourDateObj, we can simply write
theMonth = myDateObject.getMonth();
All of the methods work in a very similar way, and all values returned are based on local time, that is, local to the machine the code is running on. It's also possible to use Universal Time, previously known as GMT, but we'll save looking at this until Chapter 9.
Note that the getFullYear() method is available only in IE 4+ and NN 4.06+.
In this example, we use the get date type methods we have been looking at to write the current day, month, and year to a web page.
<html> <body> <script language="JavaScript" type="text/javascript"> var months = new Array("January","February","March","April","May","June","July", "August","September","October","November","December"); var dateNow = new Date(); var yearNow = dateNow.getFullYear(); var monthNow = months[dateNow.getMonth()]; var dayNow = dateNow.getDate(); var daySuffix; switch (dayNow) { case 1: case 21: case 31: daySuffix = "st"; break; case 2: case 22: daySuffix = "nd"; break; case 3: case 23: daySuffix = "rd"; break; default: daySuffix = "th"; break; } document.write("It is the " + dayNow + daySuffix + " day "); document.write("in the month of " + monthNow); document.write(" in the year " + yearNow); </script> </body> </html>
Save the code as ch4_examp6.htm. If you load up the page, you should see a correctly formatted sentence telling you what the current date is.
The first thing we do in the code is to declare an array and populate it with the months of a year. Why do this? Well, there is no method of the Date object that'll give us the month by name instead of as a number. However, this poses no problem; we just declare an array of months and use the month number as the array index to select the correct month name.
var months = new Array("January","February","March","April","May","June","July", "August","September","October","November","December");
Next we create a new Date object and by not initializing with our own value, we allow it to initialize itself to the current date and time.
var dateNow = new Date();
Following this we set the yearNow variable to the current year, as returned by the getFullYear() method.
var yearNow = dateNow.getFullYear();
Note that getFullYear() only became available with version 4 browsers, such as IE 4 and NN 4.06 and above. Prior to this, there was only the getYear() method, which on some browsers only returned a two-digit year.
We then populate our monthNow variable with the value contained in the array element with an index of that returned by getMonth(). Remember that getMonth() returns the month as an integer value, starting with 0 for January—this is a bonus, since arrays also start at zero, so no adjustment is needed to find the correct array element.
var monthNow = months[dateNow.getMonth()];
Finally, the current day of the month is put into variable dayNow.
var dayNow = dateNow.getDate();
Next we use a switch statement that we learned about in the last chapter. This is a useful technique for adding the correct suffix to the date that we already have. After all, our application will look more professional if we can say, "it is the 1st day," rather than, "it is the 1 day." This is a little tricky, however, because the suffix we want to add depends on the number that precedes it. So, for the 1st, 21st, and 31st days of the month, we have
switch (dayNow) { case 1: case 21: case 31: daySuffix = "st"; break;
For the 2nd and 22nd days, we have
case 2: case 22: daySuffix = "nd"; break;
and for the 3rd and 23rd days, we have
case 3: case 23: daySuffix = "rd"; break;
Finally, we need the default case for everything else. As you will have guessed by now, this is simply "th".
default: daySuffix = "th"; break; }
In the final lines we simply write the information to the HTML page, using document.write().
To change part of the date in a Date object, we have a group of set functions, which pretty much replicate the get functions described earlier, except that we are setting not getting the values. These are summarized in the following table.
Method |
Description |
---|---|
setDate() |
The date of the month is passed in as the parameter to set the date. |
setMonth() |
The month of the year is passed in as an integer parameter, where 0 is January, 1 is February, and so on. |
setFullYear() |
This sets the year to the four-digit integer number passed in as a parameter. |
Note |
Note that for security reasons, there is no way for web-based JavaScript to change the current date and time on a user's computer. |
So, to change the year to 2001, the code would be
myDateObject.setFullYear(2001);
Again, the setFullYear() method is available only in IE 4+ and NN 4.06+ browsers.
Setting the date and month to the 27th of February looks like the following:
myDateObject.setDate(27); myDateObject.setMonth(1);
One minor point to note here is that there is no direct equivalent to the getDay() method. Once the year, date, and month have been defined, the day is automatically set for you.
Take a look at the following code:
var myDate = new Date("1 Jan 2000"); myDate.setDate(32); document.write(myDate);
Surely there is some error—since when has January had 32 days? The answer to this query is that of course it doesn't, and JavaScript knows that. Instead JavaScript sets the date to 32 days from the 1st of January—that is, it sets it to the 1st of February.
The same also applies to the setMonth() method. If you set it to a value greater than 11, the date automatically rolls over to the next year. So if we use setMonth(12), that will set the date to January of the next year, and similarly setMonth(13) is February of the next year.
How can we use this feature of setDate() and setMonth() to our advantage? Well, let's say we want to find out what date it is 28 days from now. Given that different months have different numbers of days and that we could roll over to a different year, it's not as simple a task as it might first seem. Or at least that would be the case if it were not for setDate(). The code to achieve this task is as follows:
var nowDate = new Date(); var currentDay = nowDate.getDate(); nowDate.setDate(currentDay + 28);
First we get the current system date by setting the nowDate variable to a new Date object with no initialization value. In the next line we put the current day of the month into a variable called currentDay. Why? Well, when we use setDate() and pass it a value outside of the maximum number of days for that month, it starts from the first of the month and counts that many days forward. So, if today's date is the 15th of January and we use setDate(28), it's not 28 days from the 15th of January, but 28 days from the 1st of January. What we want is 28 days from the current date, so we need to add the current date onto the number of days ahead we want. So, we want setDate(15 + 28). In the third line we set the date to the current date, plus 28 days. We stored the current day of the month in currentDay, so now we just add 28 to that to move 28 days ahead.
If we want the date 28 days prior to the current date, we just pass the current date minus 28. Note that this will most often be a negative number. We need to change only one line, and that's the third one, which we change to
nowDate.setDate(currentDay - 28);
We can use exactly the same principles for setMonth() as we have used for setDate().
Retrieving the individual time data works much like the get methods for date values. The methods we use here are
These methods return respectively the hours, minutes, seconds, milliseconds, and full time of the specified Date object, where the time is based on the 24-hour clock: 0 for midnight and 23 for 11 p.m. The last method is similar to the toDateString() method in that it returns an easily readable string, except in this case, it contains the time (for example, "13:03:51 UTC").
Note that the getMilliseconds() method is available only in IE 4+ and NN 4.06+ browsers.
Let's look at an example that writes out the current time to the page.
<html> <body> <script language="JavaScript" type="text/javascript"> var greeting; var nowDate = new Date(); var nowHour = nowDate.getHours(); var nowMinute = nowDate.getMinutes(); var nowSecond = nowDate.getSeconds(); if (nowMinute < 10) { nowMinute = "0" + nowMinute; } if (nowSecond < 10) { nowSecond = "0" + nowSecond; } if (nowHour < 12) { greeting = "Good Morning"; } else if (nowHour < 17) { greeting = "Good Afternoon"; } else { greeting = "Good Evening"; } document.write("<h4>" + greeting + " and welcome to my website</h4>") document.write("According to your clock the time is "); document.write(nowHour + ":" + nowMinute + ":" + nowSecond); </script> </body> </html>
Save this page as ch4_examp7.htm. When you load it into a web browser, it writes a greeting based on the time of day as well as the current time, as shown in Figure 4-4.
The first two lines of code declare two variables—greeting and nowDate.
var greeting; var nowDate = new Date();
The greeting variable will be used shortly to store the welcome message on the website, whether this is good morning, afternoon, or evening. The nowDate variable is initialized to a new Date object. Note that the constructor for the Date object is empty, so JavaScript will store the current date and time in it.
Next, we get the information on current time from nowDate and store it in various variables. You can see that getting time data is very similar to getting date data, just using different methods.
var nowHour = nowDate.getHours(); var nowMinute = nowDate.getMinutes(); var nowSecond = nowDate.getSeconds();
You may wonder why the following lines are included in the example.
if (nowMinute < 10) { nowMinute = "0" + nowMinute; } if (nowSecond < 10) { nowSecond = "0" + nowSecond; }
These lines are there just for formatting reasons. If the time is nine minutes past 10, then you expect to see something like 10:09. You don't expect 10:9, which is what we would get if we used the getMinutes() method without adding the extra zero. The same goes for seconds. If you're just using the data in calculations, you don't need to worry about formatting issues—we do here because we're inserting the time the code executed into the web page.
Next, in a series of if statements, we decide what greeting to create for displaying to the user based on the time of day.
if (nowHour < 12) { greeting = "Good Morning"; } else if (nowHour < 17) { greeting = "Good Afternoon"; } else { greeting = "Good Evening"; }
Finally, we write out the greeting and the current time to the page.
document.write("<h4>" + greeting + " and welcome to my website</h4>"); document.write("According to your clock the time is "); document.write(nowHour + ":" + nowMinute + ":" + nowSecond);
We'll see in Chapter 9 on dates, times, and timers how we can write a continuously updating time to the web page, making it look like a clock.
When we want to set the time in our Date objects, we have a series of methods similar to those used for getting the time:
setHours()
setMinutes()
setSeconds()
setMilliseconds()
Again, the setMilliseconds() method is available only in IE 4+ and NN 4.06+ browsers.
These work in a similar way to setting the date, in that if we set any of the time parameters to an illegal value, JavaScript assumes you mean the next or previous time boundary. If it's 9:57 and you set minutes to 64, the time will be set to 10:04, that is, 64 minutes from 9:00.
This is demonstrated in the following code below.
var nowDate = new Date(); nowDate.setHours(9); nowDate.setMinutes(57); alert(nowDate); nowDate.setMinutes(64); alert(nowDate);
First we declare the nowDate variable and assign it to a new Date object, which will contain the current date and time. In the following two lines we set the hours to 9 and the minutes to 57. We show the date and time using an alert box, which should show a time of 9:57. The minutes are then set to 64 and again an alert box is used to show the date and time to the user. Now the minutes have rolled over the hour so the time shown should be 10:04.
If the hours were set to 23 instead of 9, setting the minutes to 64 would not just move the time to another hour but also cause the day to change to the next date.
In this section we'll be looking at some quite tricky and advanced stuff. It's not essential stuff so you may want to move on and come back to it at a later date.
We've seen that JavaScript provides a number of objects built into the language and ready for us to use. It's a bit like a house that's built already and we can just move on in. However, what if we want to create our own house, design it ourselves for our own specific needs? In that case we'll use an architect to create technical drawings and plans that provide the template for the new house—the builders use the plans to tell them how to create the house.
So what does any of this have to do with JavaScript and objects? Well, JavaScript allows us to be an architect and create the templates for our own objects to our own specification to fill our specific needs. Let's say, for example, we were creating a cinema booking system. JavaScript doesn't come with any built-in cinema booking objects, so we'd have to design our own. What we need to do is create objects modeled round the real world. So for a simple cinema booking system, we might have an object representing customers' booking details and an object for the cinema where the bookings have been made. As well as allowing us to store information, we can create our own methods for an object. So for a booking system, we might want an "add new booking" method or a method that gets details of all the bookings currently made.
Where we have no need to store data but simply want functionality, such as the fixDecimalPlaces() function we saw before, it's generally easier just to have a code library rather than create a special object.
Just as a builder of a house needs an architect's plans to know what to build and how it should be laid out, we need to provide blueprints telling JavaScript how our object should look. For example, we need to define its methods and provide the code for those methods. The key to this is JavaScript's support for definition of classes. Classes are essentially templates for an object, a bit like the architect's drawings are the template used to build a house. Before we can use our new object type, we need to define its class, methods, and properties. The important distinction is that when we define our class, no object based on that is created. It's only when we create an instance of our class using the new keyword that an object of that class type, based on our class blueprint or prototype, is created.
A class consists of three things:
A constructor
Method definitions
Properties
A constructor is a method called every time one of our objects based on this class is created. It's useful when we want to initialize properties or the object in some way. We need to create a constructor even if we don't pass any parameters to it or it contains no code. In that case it'd just be an empty definition. As with functions, a constructor can have zero or more parameters.
We used methods when we used JavaScript's built-in objects; now we get the chance to use classes to define our own methods performing specific tasks. Our class will specify what methods we have and the code that they execute. Again we have used properties of built-in objects and now get to define our own. We don't need to declare our class's properties. We can simply go ahead and use properties in our class without letting JavaScript know in advance.
Let's create a simple class based on the real-world example of a cinema booking system.
Let's start by creating a class for a customer's booking. Our class will be called the CustomerBooking class. The first thing we need to do is create the class constructor.
The constructor for our class is shown here:
function CustomerBooking (bookingId, customerName, film, showDate) { this.customerName = customerName; this.bookingId = bookingId; this.showDate = showDate; this.film = film; }
Your first thought might be that what we have here is simply a function, and you'd be right. It's not until we start defining the CustomerBooking class properties and methods that it becomes a class. This is in contrast to some programming languages, which have a more formal way of defining classes.
When looking at the code, the important thing to note is that the constructor function's name must match that of the class we are defining—in this case the CustomerBooking class. That way when a new instance of our class as an object (termed an object instance) is created, this function will be called automatically. Note we have four parameters for our constructor function, and these are used inside the class itself. However, note that we use the this keyword. For example
this.customerName = customerName;
Inside a constructor function or within a class method, the this keyword will refer to that object instance of our class. Here we refer to the customerName property of this class object, and we set it to equal the customerName parameter. If you have used other object-oriented programming languages, you might wonder where we defined this customerName property. The answer is we didn't; simply by assigning a property a value, JavaScript creates it for us. There is no check that the property exists; JavaScript creates it as it needs to. The same is true if we use the object with a property never mentioned in our class definition. All this free property creation might sound great, but it has drawbacks, the main one being that if we accidentally misspell a property name, JavaScript won't tell us; it'll just create a new property with the misspelled name, something that can make it difficult to track bugs. One way around this problem is to create methods that get a property's value and allow us to set a property's value. Now this may sound like hard work, but it can reduce bugs or at least make them easier to spot. Let's create a few property get/set methods for our CustomerBooking class.
CustomerBooking.prototype.getCustomerName = function() { return this.customerName; } CustomerBooking.prototype.setCustomerName = function(customerName) { this.customerName = customerName; } CustomerBooking.prototype.getShowDate = function() { return this.showDate; } CustomerBooking.prototype.setShowDate = function(showDate) { this.showDate = showDate; } CustomerBooking.prototype.getFilm = function() { return this.film; } CustomerBooking.prototype.setFilm = function(film) { this.film = film; } CustomerBooking.prototype.getBookingId = function() { return this.bookingId; } CustomerBooking.prototype.setBookingId = function(bookingId) { this.bookingId = bookingId; }
Now we have defined a set and get method for each of our class's four properties, bookingId, film, customerName, and showDate. Let's look at how we created one of the methods, the getCustomerName () method.
CustomerBooking.prototype.getCustomerName = function() { return this.customerName; }
The first thing we notice is that it's a very odd way of defining a function. On the left we set the class's prototype property's getCustomerName to equal a function, which we then define immediately afterwards. In fact, JavaScript supplies most objects with a prototype property, which allows new properties and methods to be created. So, whenever we want to create a method for our class, we simply write
className.prototype.methodName = function(method parameter list) { // method code }
We've created our class, but how do we now create new objects based on that class? Well, we look at this in the next section.
We create instances of our classes in the same way we created instances of built-in JavaScript classes: using the new keyword. So to create a new instance of our CustomerBooking class, we'd write
var firstBooking = new CustomerBooking(1234, "Robert Smith","Raging Bull", "25 July 2004 18:20"); var secondBooking = new CustomerBooking(1244, "Arnold Palmer","Toy Story", "27 July 2004 20:15");
Here, as with a String object, we have created two new objects and stored them in variables, firstBooking and secondBooking, but this time it's a new object based on our class.
Let's call the getCustomerName() method of each of the two objects and write the results to the page.
document.write("1st booking person's name is " + firstBooking.getCustomerName() + "<br>"); document.write("2nd booking person's name is " + secondBooking.getCustomerName());
And we'll see
1st booking person's name is Robert Smith 2nd booking person's name is Arnold Palmer
written into the page from information contained in our class objects. Now let's put this together in a page.
<html> <body> <script language="JavaScript" type="text/javascript"> // CustomerBooking class function CustomerBooking(bookingId, customerName, film, showDate) { this.customerName = customerName; this.bookingId = bookingId; this.showDate = showDate; this.film = film; } CustomerBooking.prototype.getCustomerName = function() { return this.customerName; } CustomerBooking.prototype.setCustomerName = function(customerName) { this.customerName = customerName; } CustomerBooking.prototype.getShowDate = function() { return this.showDate; } CustomerBooking.prototype.setShowDate = function(showDate) { this.showDate = showDate; } CustomerBooking.prototype.getFilm = function() { return this.film; } CustomerBooking.prototype.setFilm = function(film) { this.film = film; } CustomerBooking.prototype.getBookingId = function() { return this.bookingId; } CustomerBooking.prototype.setBookingId = function(bookingId) { this.bookingId = bookingId; } var firstBooking = new CustomerBooking(1234, "Robert Smith","Raging Bull", "25 July 2004 18:20"); var secondBooking = new CustomerBooking(1244, "Arnold Palmer","Toy Story", "27 July 2004 20:15"); document.write("1st booking persons name is " + firstBooking.getCustomerName() + "<br>"); document.write("2nd booking persons name is " + secondBooking.getCustomerName()); </script> </body> </html>
At the top of the page is our <script> tag, inside of which is the code that defines our class. We must include class definition code in every page that uses our class to create objects. For convenience, we may therefore decide to put our class definitions in a separate file and import that file into each page that uses the class. We can do this using the <script> tag, but instead of putting the code inside the open and close tags, we use the script tag's src attribute to point to the file containing the JavaScript. For example, if we create a file called MyCinemaBookingClasses.js and put our class code in there, we can import it into a page as shown here:
<script language="JavaScript" src="MyCinemaBookingClasses.js"></script>
The src attribute points to the URL of our class, which in this case assumes that the class's .js file is in the same directory as our page.
So far we have a class for items that we can put a single booking into, but no class representing all the bookings taken by a cinema. So how can we create a cinema class that supports the storage of zero or more items? The answer is using an array, which we discussed in the previous chapter.
Let's start by defining our class, which we'll call the cinema class, and add it to the script block with our CustomerBooking class.
// cinema class function cinema() { this.bookings = new Array(); }
Here we define the constructor. Inside the constructor, we initialize the bookings property that will hold all the CustomerBooking class objects.
Next we need to add a way of making bookings for the cinema; for this we create the addBooking() method.
cinema.prototype.addBooking = function(bookingId, customerName, film, showDate) { this.bookings[bookingId] = new CustomerBooking(bookingId, customerName, film, showDate); }
The method takes four parameters, the details needed to create a new booking. Then inside the method, we create a new object of type CustomerBooking. A reference to this object is stored inside our bookings array, using the unique bookingId to associate the place in which the new object is stored.
Let's look at how we can access the items in the array. In the following method, called getBookingsTable(), we go through each booking in the cinema and create the HTML necessary to display all the bookings in a table.
cinema.prototype.getBookingsTable = function() { var booking; var bookingsTableHTML = "<table border=1>"; for (booking in this.bookings) { bookingsTableHTML += "<tr><td>"; bookingsTableHTML += this.bookings[booking].getBookingId(); bookingsTableHTML += "</td>"; bookingsTableHTML += "<td>"; bookingsTableHTML += this.bookings[booking].getCustomerName(); bookingsTableHTML += "</td>"; bookingsTableHTML += "<td>"; bookingsTableHTML += this.bookings[booking].getFilm(); bookingsTableHTML += "</td>"; bookingsTableHTML += "<td>"; bookingsTableHTML += this.bookings[booking].getShowDate(); bookingsTableHTML += "</td>"; bookingsTableHTML += "</tr>"; } bookingsTableHTML += "</table>"; return bookingsTableHTML; }
We can access each booking by its unique bookingId, but what we want to do is simply loop through all the bookings for the cinema, so we use a for...in loop, which will loop through each item in the items array. Each time the loop executes, booking will be set by JavaScript to contain the bookingId of the next booking; it doesn't contain the item itself but its associated keyword.
Since we have the associated keyword, we can access the item objects in the array like this:
this.bookings[booking]
Remember this refers to the object instance of our class. We then use the CustomerBooking object's get methods to obtain the details for each booking. Finally, on the last line we return the HTML—with our summary of all the bookings—to the calling code.
Let's put this all together in a page and save the page as ch4_examp8.htm.
<html> <body> <h2>Summary of bookings</h2> <script language="JavaScript" type="text/javascript"> // CustomerBooking class function CustomerBooking(bookingId, customerName, film, showDate) { this.customerName = customerName; this.bookingId = bookingId; this.showDate = showDate; this.film = film; } CustomerBooking.prototype.getCustomerName = function() { return this.customerName; } CustomerBooking.prototype.setCustomerName = function(customerName) { this.customerName = customerName; } CustomerBooking.prototype.getShowDate = function() { return this.showDate; } CustomerBooking.prototype.setShowDate = function(showDate) { this.showDate = showDate; } CustomerBooking.prototype.getFilm = function() { return this.film; } CustomerBooking.prototype.setFilm = function(film) { this.film = film; } CustomerBooking.prototype.getBookingId = function() { return this.bookingId; } CustomerBooking.prototype.setBookingId = function(bookingId) { this.bookingId = bookingId; } // cinema class function cinema() { this.bookings = new Array(); } cinema.prototype.addBooking = function(bookingId, customerName, film, showDate) { this.bookings[bookingId] = new CustomerBooking(bookingId, customerName, film, showDate); } cinema.prototype.getBookingsTable = function() { var booking; var bookingsTableHTML = "<table border=1>"; for (booking in this.bookings) { bookingsTableHTML += "<tr><td>"; bookingsTableHTML += this.bookings[booking].getBookingId(); bookingsTableHTML += "</td>"; bookingsTableHTML += "<td>"; bookingsTableHTML += this.bookings[booking].getCustomerName(); bookingsTableHTML += "</td>"; bookingsTableHTML += "<td>"; bookingsTableHTML += this.bookings[booking].getFilm(); bookingsTableHTML += "</td>"; bookingsTableHTML += "<td>"; bookingsTableHTML += this.bookings[booking].getShowDate(); bookingsTableHTML += "</td>"; bookingsTableHTML += "</tr>"; } bookingsTableHTML += "</table>"; return bookingsTableHTML; } var londonOdeon = new cinema(); londonOdeon.addBooking(342, "Arnold Palmer","Toy Story", "15 July 2004 20:15"); londonOdeon.addBooking(335, "Louise Anderson","The Shawshank Redemption", "27 July 2004 11:25"); londonOdeon.addBooking(566, "Catherine Hughes", "Never Say Never", "27 July 2004 17:55"); londonOdeon.addBooking(324, "Rob Gordon", "Shrek", "29 July 2004 20:15"); document.write(londonOdeon.getBookingsTable()); </script> </body> </html>
Our new code is the lines
var londonOdeon = new cinema(); londonOdeon.addBooking(342, "Arnold Palmer","Toy Story", "15 July 2004 20:15"); londonOdeon.addBooking(335, "Louise Anderson", "The Shawshank Redemption", "27 July 2004 11:25"); londonOdeon.addBooking(566, "Catherine Hughes", "Never Say Never", "27 July 2004 17:55"); londonOdeon.addBooking(324, "Rob Gordon","Shrek", "29 July 2004 20:15"); document.write(londonOdeon.getBookingsTable());
These create a new cinema object and store a reference to it in the variable londonOdeon. We then create four new bookings using the cinema class's addBooking() method. On the final line, we write the HTML returned by the getBookingsTable() method to the page.
Our page should now look like that shown in Figure 4-5.
The cinema booking system we have created is very basic to say the least! However, it gives you an idea of how JavaScript classes can be used to help make code more maintainable and how they can be used to model real-world problems and situations.