JavaScript for Beginners Copyright © 2005 Jerry Stratton http://www.hoboes.com/NetLife/Web_Scripting/ http://www.hoboes.com/NetLife/Web_Scripting/Web_Scripting.pdf Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1. A copy of the license is included in the section entitled “GNU Free Documentation License” October 7, 2008
Getting Started
2
Getting Started JavaScript allows you to create ‘dynamic, interactive’ web pages: web pages that do things in response to what your readers do, without having a separate page for each possible response. In this tutorial, I’m going to cover some of the very basics of using JavaScript in your web pages. I’m going to assume that you know a little bit about HTML, but nothing about programming or JavaScript. If you aren’t familiar with HTML, you’ll want to go over the Web Writing Guide at http://www.hoboes.com/NetLife/Web_Writing/ first. First, JavaScript has nothing to do with Java. Java is a separate programming language. JavaScript is a language for scripting web browsers and web servers, although it is seeing other uses as it becomes popular. Java was created by Sun and is designed for complete software applications, while JavaScript was created separately by Netscape Corporation and is designed for modifying (and “scripting”) web pages.
The Basic Web Page The first thing we need to do is create two files for the tutorial: a web page, and a JavaScript file. You can call them anything you want. I’m going to call the web page “basicscripting.html” and the JavaScript file “scripts.js”.
basicscripting.html
Basic JavaScript Tutorial <script src="scripts.js" language="JavaScript">
Basic JavaScript Tutorial
We will use this page to test out our dangerously revolutionary JavaScript programs.
This is about as simple a web page as you’ll ever make. The only thing out of the ordinary is the <script> tag inside the ‘head’ portion of the web page. Even that looks familiar if you’ve used the
tag. The “script” has a “source” and a “language”. The language is obvious: we’re using JavaScript. The “source”, or “src” is a file or URL to a file that stores scripts this page will use. In this case, we’re telling the browser that the scripts this page uses are stored in a file called
3
Getting Started
“scripts.js”. The browser will go to our site and ask for that file in the same directory as the web page. You can put any URL inside the “src” modifier, as long as it points to a valid JavaScript file.
scripts.js //these are my basic javascripts
That’s it for “scripts.js”: we haven’t created any JavaScripts yet! In a JavaScript file, two slashes (“//”) tell the web browser to completely ignore that line. Every line that begins with a “//” is a “comment”. It isn’t JavaScript, and the browser would likely get in trouble if it tried to be dynamic with that line. JavaScript is line oriented. Unlike HTML, which uses opening tags and ending tags, such as and , to identify the beginning and ending of things, JavaScript uses semicolons and carriage returns. So, if you have the following file as a JavaScript file: //this is a comment this is another line
You are going to get an error. The browser is going to assume that the second line, “this is another line” is some JavaScript code, but it isn’t. Always be careful with carriage returns when writing JavaScript.
Alerts One of the easiest and most useful things done with JavaScript is to alert you, the programmer, to some aspect of your script, or alert the viewer that they’re possibly about to do something wrong. You might display a warning if they didn’t fill in some required portion of a form, for example. The JavaScript “code” for an alert is very simple. It is “window.alert” and then the alert message you want to display. If you write “window.alert("Paul is sleeping")” in your JavaScript, a window will pop up and display the phrase “Paul is sleeping”. Let’s go ahead and write our first bit of JavaScript code. Add the following to your “scripts.js” file: //displays message in alert window function w_alert(w_message) { window.alert(w_message); return false; }
Getting Started
4
The first line is a comment. The browser will ignore the line “displays message in alert window”, but you won’t. You’re who its there for. A few months later when you look at the “scripts.js” file, you will know what this JavaScript code does: it displays a message in an alert window. That’s what comments are for. They “comment” your JavaScript code to remind you what this code does. The second line tells the browser that we are creating a “function” called “w_alert”, and when we use “w_alert”, we’re going to give it one thing to work with, which it will store in “w_message”. By calling this a “function”, we are telling the browser not to perform these lines of JavaScript until we ask it to perform the function called “w_alert”. By giving it one “parameter”, or “thing to work with”, we are making it more useful to us. We can have it display any message we want, rather than having to write one function for “hello” and another function for “goodbye”. When we use this function, we “call” it by writing “return w_alert("The Message we want");”. If we want it to say “hello”, we would write “return w_alert("hello");”. We put the message in quotes between the two parentheses. The third line, “window.alert(w_message);”, tells the browser to pop a message up with the “variable” w_message. A variable is a place to store information. Because w_message was listed on the “function” line, anything we write between the parentheses when we “call” w_alert will be stored in “w_message”. So, whatever is in “w_message” gets displayed in the alert window. The fourth line is “return false;”. Functions do not have to return anything, but usually they do. And, usually, functions that get used on web pages return either “true” or “false”. In this case, “false” tells the browser that the function handled everything, so that the browser doesn’t follow that link. If we wrote “return true” here instead, our function would display the alert message, and then after the viewer clicked to dismiss the message, the browser would follow the link. Often when you display an alert, it is because you don’t want the browser to do what it normally would have done (such as submit a form). You’ll notice that each of the lines that does something ends in a semicolon. JavaScript requires this. Each JavaScript step must end in a semicolon. The function itself is surrounded by two curly brackets (“{ }”). Curly brackets in Javascript surround chunks of JavaScript code that belong together. In this case, our two lines of JavaScript are surrounded by curly brackets to indicate that they belong in the function called “s_display”.
5
Getting Started
We are ready to use our first JavaScript. Go back to “basicscripts.html” and add the following link:
Negative Space
Remember that carriage returns are important, so everything between “onClick” and the final “)'” need to be on the same line! If you save this file, reload the page in your browser, and click the link, you should see a message pop up saying “You are not allowed to visit Negative Space!”. See an example of how this should work at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson1.html. The “pieces of information” that we send to functions are called “parameters” or “arguments”. Functions don’t have to take parameters, in which case the parentheses after the function name will be empty. Later on we’ll get to functions that take multiple arguments.
Image Placeholder That’s all very simple. But it doesn’t make any fundamental changes to the web browsing experience. Let’s go ahead and make something that displays images without traveling from page to page. We’ll make a single page, with a single image, but that image will change depending on what ‘link’ the user chooses. First, we need to add another “function” to our “scripts.js” file: //changes an image to another image function change_img(imgName,imgFile) { document[imgName].src = imgFile; return false; }
What’s different about this function than our previous two? First, it has a different name: “change_img”. Second, this function takes two arguments instead of one or none. It calls these arguments “imgName” and “imgFile”. It has a completely different middle line: “document[imgName].src=imgFile”. What does that mean? Well, “document[imgName]” is the image that we want to change. In order to use the function “change_img”, our images have to be able to be identified to the web browser. They have to have a name. You’ll give your images names by adding “name=something” to the HTML code for
Getting Started
6
the
tag. Once your image has a name, JavaScript can do things to that image by calling it “document[the name]”. In this case, we’ve stored the name inside “imgName”, so “document[imgName]” is the image that we’re going to change. If you’ll recall from HTML, each
tag has a “src=” option that gives the URL where the web browser can find the image. We’re changing the “src” for our named image to whatever is inside “imgFile”. Let’s say we have an image on our web page called “gallery”. If we want to change the image inside “gallery” to the image at “http://www.hoboes.com/jerry/Gallery/Lego_Thumb.jpg”. We would call our “change_img” function as: change_img("gallery", "http://www.hoboes.com/jerry/Gallery/Lego_Thumb.jpg"). Go ahead and make up a new web page:
JavaScript Image Tutorial <script src="scripts.js" language="JavaScript">
JavaScript Image Tutorial
Save this file, and try clicking on the links. Instead of changing pages, what you should see is that only the image on the right changes when you click on the link. See the example at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson4.html. You’ve probably already guessed that “onClick” is the “event” that happens when someone clicks on a link. The function we call is “change_img”. We give change_img the arguments “gallery”, which is the name of the image that we want to change, and “this.href”, which is… well, what is it? In JavaScript, “this” refers to the current object. An “object” is a part of your web page. In this case,
7
Getting Started
the current object is the link to the image. That URL to the image is stored in the option called “href”. So, “this.href” is the URL to the image that this link points to. Because we “return false”, the web browser never actually follows the link. If, of course, the person viewing your page has JavaScript turned off, their browser will completely ignore our JavaScript, and it will simply visit the image the “old-fashioned way”. This is important: we have provided an improved means of viewing our images for those who want it, but those who don’t want it can still view the images, with no added work on our part. When designing JavaScript for your web pages, remember that not everyone has JavaScript, and not everyone who has JavaScript wants to use it. (Unfortunately, some commercial pages do annoying things with JavaScript, like pop up windows in front of the page you’re trying to read. This causes many people to turn JavaScript off completely, avoiding those annoyances, but also avoiding your tastefully presented JavaScript.) By using “this.href” to reference our image, we allow non-JavaScript readers to view our images, without any added work on our part. Always remember to keep things as simple as possible. JavaScript allows you to make your web pages impossible to manage if you aren’t careful. If you play around with the above and plug in some differently-sized images, you’ll notice that some image(s) have to be stretched or squeezed to match the dimensions of the first image. That’s one of the constraints on changing images: the web browser makes space for the image based on the first image, and then forces all subsequent images to change their size to match.
Understanding JavaScript
8
Understanding JavaScript Now that you have a good idea about some specifics of JavaScript, and how useful it is, its time you learned a bit about how it works.
The Object Model JavaScript assumes that there are “objects” on your page. Each part of your web page is an object that JavaScript can use. An image is an object, a link is an object, and the web page itself is an object. So is the window that the web page is in. Some objects have to be named: images and links, for example, if you want to access them from another object on the page, will have to have a “name” set with “name="something"”. The web page is called “document”. You can always access the current web page by calling for the “document”. The window is called “window”; you can access that by calling for “window”. Objects can contain other objects. The “window” object contains an object called “history” that has information about the web pages you’ve been to recently. If you want to access the “history” object, you use “window.history”. Objects have properties and functions built in to them. Usually, you will recognize the properties as corresponding to the things that are inside the object’s tag. An
object, for example, has a “src” property. An
tag has a “href” property. You use these properties with object.property. So if you are working inside of a link (as we do above), and you want the “href” property, you would use “this.href”. “This” is always the “current” object. The one we are inside. Objects also have properties that you don’t normally set. The web page, for example, has a “last modified” property, that tells the web browser when the page was last changed. The web browser uses this information to know whether or not to reload the page or use the “cached” version of the page. Because you call the web page “document”, you can access the time the page was last changed as “document.lastmodified”. We’ll be using this in the next section to create an automatic “time stamp” for our web pages. We haven’t used any object functions yet. We’ve only created our own, standalone functions. When we do use object functions, they look a lot like a cross between functions and object properties. The “window” object, for example, has an object built into it called “history”, which contains the list of
9
Understanding JavaScript
pages that the browser has been to recently. If you want to tell the browser to go back to the previous web page, you would use the “back()” function on the “history” object of the “window” object: “window.history.back()”. You can give object functions “arguments” just like we’ve already done with our custom functions. For example, the “window” object has a function called “alert”. The “alert” function pops up a dialog box with a message on it. We need to tell “alert” what that message is: “window.alert("If you leave this page, I will no longer like you.")”.
The Event Model “Events” are “things that happen on a web page”. If you click the mouse on a link, an “onClick” event takes you to the new page. If you move the mouse over a link, an “onMouseOver” event displays the URL in the status bar, and then when you move the mouse away, an “onMouseOut” event clears the status bar. Events get “sent” to objects, and then the object decides what to do with that event. What we’ve been doing is intercepting events. The web browser already had something in mind for all the events we’ve intercepted. On “mouseover” a link, it shows the URL; on “click”ing a link, it moves to that link. We’ve intercepted those events (“onMouseOver”, “onClick”) so that the web browser does something different. JavaScript normally works almost exactly opposite HTML. HTML does something and that’s it. It displays a page and it is done. The only thing the reader can do is read this static page, or click a link and go to another page. With JavaScript, you can react to what the user does: as long as you can find an event that corresponds to what the use has done and an object to intercept that event.
Scripting JavaScript is a programming language. It is generally much less forgiving than HTML. Commands end with semi-colons. Carriage returns are important. Curly brackets enclose blocks of JavaScript code. You can’t play around with those rules and expect to get a working JavaScript web page. You will also notice that I “indent” parts of JavaScript code. Usually, anything inside of curly brackets gets indented by one tab more than the line just above it:
Understanding JavaScript
10
function bless_me(saintname) { if (saintname == "Helena") { window.alert("You are blessed by the shroud. "); } else { window.alert(saintname + " blesses you. "); } }
This indentation is not necessary. If you leave it out, your programs will work just fine. However, indenting like this makes it much, much easier to see what your program is doing. You will always want to indent in a standard format (which usually means using tabs).
Loops One of the advantages of using a programming language is its ability to automate tasks that would otherwise require repetitious typing. For example, suppose we have several different sets of images to display, and we often add or subtract an image from the list. Rather than adding a new line to our list each time, as we did above, we could keep a list of just the images, and create a JavaScript function to turn our simple list of images into an HTML list. Remove the list from your HTML code: everything from the opening “”. Replace it with: <script language="JavaScript"> imageList = { "Christmas_Thumb": "Christmas", "Lego_Thumb": "Legos", "Gender_Thumb": "Lenses of Gender" }; listImages('http://www.hoboes.com/jerry/Gallery', 'gallery', imageList);
Even though this is a programming language rather than HTML, it already looks easier to create and easier to understand than the previous, HTML list with embedded JavaScript events. If we choose to add a fourth image to this list, we need only add a line with the image’s filename and the image’s title text. Note that each line except for the final line ends with a comma. This is an “associative array”, which means that this is a list where the first item in each pair is “associated with” the second item in the pair. Lego_Thumb is associated with Legos, for example. This is only half of the solution, however. The last line inside the script tag calls a function named “listImages()” that takes three options: the base of each image’s URL (most likely the folder the images are stored in if you were to use this on your own site), the name of the image display img tag, and the list of images we want the reader to choose from. //imgFolder is the base of the URL for the images
11
Understanding JavaScript //imgName is the name of the img tag that will display the images //imgList is the list of image files we want to display, in the form "image name":"image filename" function listImages(imgFolder, imgName, imgList) { document.write(""); for (currentImage in imgList) { aText = imgList[currentImage]; aHREF = imgFolder + "/" + currentImage + ".jpg"; onClick = "onClick=\"return change_img('gallery', this.href);\""; aTag = '' + aText + ''; listItem = "- " + aTag + "
"; document.write(listItem); } document.write("
"); }
See the example at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson4a.html. You’ll see a lot of “document.write()” in these examples. That’s something that documents—that is, web pages—can do in JavaScript: insert text into themselves. Wherever document.write() is called, some HTML is being inserted into the web page. At the beginning of this function we write an opening list tag and at the end we write the closing list tag. for (currentImage in imgList) { }
The meat of this function is inside a “for” loop. Loops begin and end with { and }, just as functions do. The stuff inside the loop gets done over and over again. We’ll see different kinds of loops later, but the “for” loop goes through its contents once for every item in the list. The list is the container after the keyword “in”. In this case, it is our list of images and image titles. The word listed before the “in” keyword is a container that contains each item of the list as we go through it. In this case, the first time we go through the loop, “currentImage” contains one item; the next time, it contains another item; and so on, until we have looped once for every item the imgList list contains. aText = imgList[currentImage];
Recall that when we created the image list, we did so using pairs: the image’s file name (without the “.jpg”) and the text that we want to appear on the link that displays the image. The currentImage container contains only the first item in that pair. To get the second item, we need to ask the list for it. When we use “listname[keyword]” we are asking the list for the item that is paired with that keyword. The keyword is always the first item in the pair of items. The result is always the second item which is associated with that keyword. aHREF = imgFolder + "/" + currentImage + ".jpg"; onClick = "onClick=\"return change_img('gallery', this.href);\""; aTag = '' + aText + ''; listItem = "
" + aTag + "";
Understanding JavaScript
12
These four lines construct the list item, what in our original HTML was “
…”. The first two lines construct the parts of the link (the “a” tag). The third line constructs the “a” tag from those parts. The fourth line constructs the full “li” tag. Each line does this by adding together the pieces. The third and fourth lines use the parts that were constructed in the preceding lines. document.write(listItem);
Finally, we use another “document.write()” to write out the list item. Thus, for each image in the list we will write one “
…” tag. By construction our list using a function, we not only make it easier to add and remove images from the list, we can also more easily change our mind later. For example, we might choose to display them as a table, with thumbnails. Rather than modifying each page that displays a set of images, we need modify only the function that each page calls. Note also, of course, that by doing it this way anyone who does not have JavaScript will not be able to see that list of images displayed on their browser.
13
Basic JavaScript
Basic JavaScript You should play around with the previous JavaScripts. Incorporate them into a “real” web page or two. When you feel familiar with them, we can start doing some more intensive programming.
A Smart “File Stamp” It is very useful to have a “generic” part for your web pages, something that stays basically the same throughout each page. This “footer” or “header” can include a link back to your home page, the last date the page was updated, perhaps a link to the parent page if this is a subsidiary page on your web site. Let’s create a JavaScript that creates a table of this information on our web page. Add the following function to your “scripts.js” file: function filestamp() { homePage=new String('/NetLife/'); document.write('
'); document.write('', document.title, ' |
'); document.write('Home |
'); document.write('
'); }
The first line creates a “String” object, calls it “homePage”, and gives it the value “/NetLife/”. What does that mean? Remember that JavaScript deals with objects. A “String” object is a collection of text. Your name could be a String object; your home town; just about anything normal that isn’t something which you can add or subtract. Things you can add, subtract, multiply, etc., are “Numbers”. Your income and your height in inches are numbers. Your phone number or social security number are strings. In this case, we made a new String and we put “/NetLife/” into it. You will actually want to put your own home page path into. This tutorial site’s home page is http://www.hoboes.com/NetLife/ and the path is the part after the hostname, that is, /NetLife/. If your home page is http://www.sandiego.edu/~brunetti/, your home page path is /~brunetti/. We will use the “homePage” String later in this program. document.write('
');
If you remember your HTML code for tables, you’ll recognize the part inside the straight quotes as the beginning of a right-aligned table with a thin border. “write()” is a function on the “document” object. It inserts HTML code into the web page (the “document”). What does it write? It writes
Basic JavaScript
14
whatever arguments you give it. You can give it as many arguments as you want. It will write each one, in order, to your web page. document.write('', document.title, ' |
');
If you count them, you’ll see three arguments given to “document.write()”. The first argument opens the table row and the table header; the second argument is something called “document.title”, and the third argument closes the table header and the table row. What is “document.title”? Remember that “document” is our web page. So “document.title” is the title of our web page. It is whatever we put between the “” and “” in the “” area of the web page. The next line is just like the previous one, except that it puts a link to the home page inside of a normal table cell. The last line closes the table. Now, let’s make a web page to put use this script on. Call it “advanced.html”: JavaScript Programming Tutorial <script src="scripts.js" language="JavaScript"> <script language="JavaScript"> filestamp(); Making a Global File Stamp
Of course the unexpected often looks as if it were expected. Some indeed expect the unexpected. Such is life. This leaves me in a pretty pickle and I hope you won’t refer me and my questions to Sheet, for Sheet, according to this paper has been silent since last night.
Your questions? You know the loan was given to the shipyard don’t you?
And you should get a ‘file stamp’ on the right, top of
Scripting Your Web Pages Home
your page, much like I have one to the right of this paragraph. See http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson5.html for the example.
Last Update Many web pages on the net tell you when they were last updated. This is commonly done by the web designer actually typing in the new date whenever they make a change. But that’s prone to failure, because it is easy to forget. The better way is to use “server-side includes” to have the web server automatically add this information. If you don’t want to or can’t use server-side includes
15
Basic JavaScript
(most free web hosts don’t support them, for example), you can do the same thing with javascript. So, let’s add a “Last Updated On” line to our file stamp. We’ve already used one property of our web page: document.title. The date and time that the web page was last changed is in another property, document.lastModified. So, let’s add another line to our “filestamp” script function: function filestamp() { … document.write("",document.title," |
"); document.write("Last Updated: ",document.lastModified," |
"); … }
If you’re doing this from your local hard drive, you might see a really old date or no date at all. That’s
Scripting Your Web Pages Last Updated: Wed, 29-May-2002 17:06:15 GMT Home
because browsers get this information from the server, and if you’re doing this from your hard drive, there is no server. If that happens, upload the script to your web account and try from there. See http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson6.html for the example. Suppose we want to reformat the date to something smaller? Let’s change it so that it just gives the month/day/year. This requires creating a function that takes a date as its “argument” and returns the date in the format we want, in this case just month/day/year. JavaScript has a number of built-in functions that give us the information we need. There is a “getFullYear()” function, a “getMonth()” function, and a “getDate()” function to get the year, month, and day of the month from a date. That’s all the information we need, but it isn’t quite as easy as that. You may have heard about javascript being “object oriented”. That means that it has “objects” that can do things. A date is also an object, and it can do things to itself: such as give you the year, month, and day of the month for that date. The “getFullYear()” function is attached to all date objects, and you need a date object in order to use it. Even though it looks like one, document.lastModified is not a date. It is a bunch of text that looks like a date. To get the year, month, and day out of it, we need to convert the text into what JavaScript recognizes as a date. The command to do this is: myDate = new Date(document.lastModified);
The “new” function creates a new object. This is going to be a “Date” object, so the full command is “new Date()”. And the “new” command, with respect to “Date” is very intelligent: it can take a text
Basic JavaScript
16
string that looks like a date, and create a date object from that text string. By using “new Date(document.lastModified)”, we are getting a date object that is the exact date and time that our web page was last changed. In order to use this new date object, we have to give it a name, so we can refer to it later. Like our “homePage” string, we will use “changeDate = new Date(document.lastModified);” to create a Date object called “changeDate”. Let's go ahead and add that line to our filestamp() script. Put it above all the “document.write()” lines, below the “homePage=” line. homePage=new String('/NetLife/'); changeDate = new Date(document.lastModified);
Now, you can’t just replace “document.lastModified” in the document.write line with “changeDate”. Well, you can, but you’ll get an even longer date line than you had before (go ahead and try!). We need to make a function that takes our new date object and returns just the month, day of month, and year. Let’s call it formatDate(). Change “document.lastModified” in the “document.write()” line that includes it to “formatDate(changeDate)”: document.write('Last Updated: ', formatDate(changeDate), ' |
');
Next, we have to write the “formatDate()” function: function formatDate(theDate) { thisyear = theDate.getFullYear(); thismonth = theDate.getMonth(); thismonth++; thisday = theDate.getDate(); dateformatted=thismonth + '/' + thisday + '/' + thisyear; return dateformatted; }
And that should give you a date line in your “stamp” that looks like, for example, 6/29/2008! See http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson7.html for the example. Some of our new “formatDate()” function (in fact, probably most of it) is a bit confusing. Let’s take this function on, one step at a time. function formatDate(theDate) {
This creates the function called formatDate, and specifies that it can receive one “argument”, which will be called “theDate” inside of this function. When we call the “formatDate()” function, we might call it with “formatDate(changeDate)” or “formatDate(currentDate)” or even
17
Basic JavaScript
“formatDate(birthDate)”. But whatever “argument” we send “formatDate()”, it will be called “theDate” inside of the function. thisyear = theDate.getFullYear();
This creates a number called “thisyear” which is the “year” portion of “theDate”. You might expect this function to be “getYear()”, but due to historical reasons, getYear() is “deprecated”, which means, don’t use it. thismonth = theDate.getMonth();
Next, we get the month, as a number, from theDate. thismonth++;
Computers have a tendency to start at 0 whenever they can. In JavaScript, months go from 0 to 11, not 1 to 12. Humans, of course, go from 1 to 12. The line “thismonth++;” adds one to the variable “thismonth”. (You could also use “--” to reduce a number by one.) thisday = theDate.getDate();
And we get the day of the month. Even in JavaScript, the day of the month starts at one, so we don’t need to use “++” to increase it by one. Believe it or not, this is consistent. It makes sense to start the month at 0, but the day of the month at one. We’ll get to why this is consistent later, when we talk about keeping lists of items. For now, just remember that the months start at 0, and the days start at 1. dateformatted=thismonth + '/' + thisday + '/' + thisyear;
We create a line of text that has the month, a slash, the day, a slash, and the year. We put this line of text into a line of text called “dateformatted”. return dateformatted;
We return that line of text to whatever part of our script called the “formatDate()” function. }
The final “}” ends the function. You should have a script file that looks sort of like this: //time stamp functions function filestamp() { homePage = new String('/NetLife/'); changeDate = new Date(document.lastModified); document.write(''); document.write('', document.title, ' |
'); document.write('Last Updated: ', formatDate(changeDate), ' |
'); document.write('Home |
');
Basic JavaScript
18
document.write("
"); } function formatDate(theDate) { thisyear = theDate.getFullYear(); thismonth = theDate.getMonth(); thismonth++; thisday = theDate.getDate(); dateformatted=thismonth + '/' + thisday + '/' + thisyear; return dateformatted; }
Smart Date Now that we’ve created a “formatDate()” function, we can make that function smarter. Why not have it say “Today” if the date we sent it (the date we last modified our web page) is today? And “Yesterday” for yesterday? This is easy to do, but we need to do a lot more “checking” inside of our “if” statement. We’ve already used “if” to do something if a certain condition is met. But we can also do one thing if one condition is met, otherwise do a second thing if a second condition is met, and if neither of those conditions are met, do a final thing instead. Change the top and bottom of your formatDate() function to: function formatDate(theDate) { today = new Date(); oneDay = 24*60*60*1000; UTCDiff = today.getTimezoneOffset()*60*1000; msToday = today.getMilliseconds() + today.getSeconds()*1000 + today.getMinutes()*60*1000 + today.getHours()*60*60*1000 - UTCDiff; if (theDate >= (today - msToday)) { dateformatted = 'Today'; } else if (theDate >= today - msToday - oneDay) { dateformatted = 'Yesterday'; } else { thisyear = theDate.getFullYear(); … dateformatted=thismonth + '/' + thisday + '/' + thisyear; } return dateformatted; }
Our original “formatDate()” function is now within the last “else do this” section of our “if” statement. Determining whether a file was last modified “today” or “yesterday” is not as easy as it sounds at first: we can’t just check to see if the file was modified within the last 24 hours–24 hours ago is usually yesterday, not today. We need to determine when today started, and then see if the file was modified after today started. If so, the file was modified today. If not, we see if the file was modified in the 24-hour period before today. If so, it was modified yesterday. Otherwise, we create
19
Basic JavaScript
the date it was modified and show that. See http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson8.html for the example. Dates in most programming languages are stored as numbers: they are usually stored as a certain number of days, seconds, or milliseconds from some arbitrary starting point. JavaScript uses milliseconds. Any date in JavaScript can be represented by the number of milliseconds that date is from the “starting date”. (The date of the starting date is immaterial, since you will always be calculating “off of” it. It might even change from client to client, but you don’t care.) today = new Date();
If you ask for a new Date and don’t specify what that date is, you get right now. In JavaScript, a “Date” is really a date and a time. You’ll recall from our first timestamp that the date modified included the actual time the file was modified. oneDay = 24*60*60*1000;
In order to determine whether the file was modified yesterday, we need to know what number makes up a day. Since JavaScript stores dates in milliseconds, we ask for 24 (hours per day), times 60 (minutes per hour), times 60 (seconds per minute), times 1000 (milliseconds per second). You could simply calculate that number on your calculator and then use it directly, but storing the number in a variable called “oneDay” makes it easier to remember what that number means when you look at your code later on. UTCDiff = today.getTimezoneOffset()*60*1000;
Web page dates are stored in Universal Time (GMT: Greenwich Mean Time). If we want to know when the file was modified in the client’s local time, we need to determine the time zone offset. The time zone offset is given in minutes, so we need to multiply by 60 to get the seconds, and then by 1000 to get the milliseconds. msToday = today.getMilliseconds() + today.getSeconds()*1000 + today.getMinutes()*60*1000 + today.getHours()*60*60*1000 - UTCDiff;
Our “today” contains the date and time right up to now. This line determines what we need to remove from “today” to get the beginning of today. If it is 5:40 AM and 17 seconds and 553 milliseconds, this will (before the “UTCDiff”) give us 553 milliseconds plus 17 seconds plus 40 minutes plus 5 hours, converting each number into milliseconds. The calculation then removes the time zone difference. If we substract our “msToday” from our “today”, we get the beginning of today in local time. if (theDate >= (today - msToday)) { dateformatted = 'Today';
Basic JavaScript
20
Our first “if” checks to see if “theDate” (which you’ll recall is the date our file was modified) is after or equal to the beginning of today. If so, it sets “dateformatted” to the word “Today”. } else if (theDate >= today - msToday - oneDay) { dateformatted = 'Yesterday';
If the first condition does not match, we check to see if “theDate” is after or equal to the beginning of today minus 24 hours. If so, we set “dateformatted” to the word “Yesterday”. } else { thisyear = theDate.getFullYear();
Finally, if neither of the first two conditions match, we do our previous calculation to create a string that represents the date that the file was modified. It’s a good idea to test more than one condition. Right now, since we’re editing this page, we should expect it to say “Today”. But we should also test what’s going to happen tomorrow and the day after. After the line that says “oneDay = …”, add these two comments: //today = new Date(today.getTime() + oneDay) //today = new Date(today.getTime() + oneDay*2)
Remove the slashes in front of the first line to check what will happen tomorrow; remove the slashes in front of the second line to check what will happen the day after tomorrow. Put the slashes back once you’re done testing. The “getTime()” function returns the number of milliseconds since JavaScript starts counting time. By adding oneDay to it or twice oneDay to it, we can pretend that it is tomorrow or the day after tomorrow.
Current Time It can be useful, especially for dynamic pages, to show the current time on the web page. This lets the viewer know how up to date the information is—when they last reloaded the web page. We can format a “current time” date just like we formatted a date-only date. Add the following line to your “filestamp”: document.write('Current Time: ', formatTime(new Date()), ' |
');
This calls a function called “formatTime()” with an argument of “new Date()”. We already know what “new Date()” does: it gets the current time. So we’re sending the current time to our new formatTime() function. Let’s create the function.
21
Basic JavaScript function formatTime(theDate) { thishour = theDate.getHours(); thisminute = theDate.getMinutes(); if (thishour > 11) { thishalf='PM'; } else { thishalf='AM'; } if (thishour > 12) { thishour -= 12; } if (thisminute < 10) { thisminute = '0'+thisminute; } timeformatted = thishour + ': ' + thisminute + ' ' + thishalf; return timeformatted; }
You should be able to follow this from the description of “formatDate()”. It does the same thing, but with hours and minutes instead of month, day, and year. We check the hour to see if it is AM or PM (and then substract 12 from the hour if the hour is 1 PM or later). We make sure the minutes are zero-padded on the left. And then we return a string that looks like “5:32 PM”. See http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson9.html for the example.
Using Lists Our smart “formatDate()” function tells us if the date was yesterday or today. Why not also make it return the name of the weekday if the date is within six days past? This is relatively simple to check for: all we need to do is add an extra “else if” to our list of conditions, and see if the document’s modification date is within the last six days. We could use one “else if” for each possible day, but this is time consuming and prone to error—and we don’t even really know what day three days past was. A better choice will be to determine what the current day is, offset this by the number of days past, and then check a list of weekdays to get the weekday name. That is, we’ll work with numbers first, because that’s what computers are good at. Once we get the number (i.e., the second day, third day, etc, of the week) we’ll look at a list and get the appropriate item from the list. Add our new JavaScript code after the part where we check for yesterday: } else if (theDate >= today - msToday - oneDay) { dateformatted = 'Yesterday'; } else if (theDate >= today - msToday - oneDay*6) { weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
Basic JavaScript
22
theWeekDay = theDate.getDay(); dateformatted = weekDays[theWeekDay]; } else { …
This is pretty simple. The new part is the list of weekdays. } else if (theDate >= today - msToday - oneDay*6) {
If “theDate” isn’t today and it isn’t yesterday, we see if it is one of the last six days, by multiplying “oneDay” by 6 and subtracting it from today. If theDate is greater than the result, we know that theDate is within six days of today. weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
Computers tend to work with numbers, but telling people that this page was last modified on weekday “6” isn’t very user-friendly. Here, we create a list of weekdays in English (more advanced JavaScript programmers would check the user’s language and create the list in that language). Once we have a “list” (they’re usually called “arrays” by programmers), we can ask the computer for the first, second, third, etc., item from the list. The trick is that JavaScript, like many computer languages, starts counting from zero instead of from one. The first item in a list is item 0. The second item is item 1. As you get into more advanced programming you’ll find that this is a useful feature. For now, though, it’s probably confusing. Just remember that JavaScript starts counting at zero and you’ll be okay. When you ask for an item from a list, you do so with the list’s name (in this case, “weekDays”) and the number of the item in brackets: “weekDays[3]” is the same as “Wednesday”. Wednesday is item 3 (the fourth item) in our list of weekdays. Where lists really become useful is that we can do that asking with a variable. If we have a variable called “payday” and it has “5” in it, “weekDays[payday]” will give you “Friday”. And that’s what we’re about to do. theWeekDay = theDate.getDay(); dateformatted = weekDays[theWeekDay];
When we ask the “theDate” object to “getDay()”, it will return a number from 0 to 6, with zero being Sunday and 6 being Saturday. So we take that number and ask our “weekDays” list for the corresponding item in the list. That gives us the weekday when our web page was last modified. See http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson10.html for the example.
23
Advanced JavaScript
Advanced JavaScript Working With Strings So far we haven’t done much with strings except to add them together (“concatenate” them in programmers’ parlance) to make larger strings. We can also take them apart to make smaller strings, and look inside them to see what they contain. It is useful to always show the user a “parent” page for the page they’re currently viewing. Often your viewers come to your site from a search engine, and they don’t necessarily come in to your site’s main page. By providing a parent page link, you give the viewer the option of switching “up” to a more general view of your site, without going all the way up to the home page and then navigating back down again (assuming they even know how to navigate back down). Every page except for your home page has a “parent” page, and if you have designed your site well, the “parent” page is in the same folder as the current page, or is in one folder up if your visitor is already visiting your main page for that folder. For example, the parent page of http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson5.html is http://www.hoboes.com/NetLife/Web_Scripting/lessons/. And the parent page of that is http://www.hoboes.com/NetLife/Web_Scripting/. The parent page of that is http://www.hoboes.com/NetLife/ and that has no parent page, since it is the tutorial section’s home page. Of course, we wouldn’t want to show the parent page if it is the same as the home page. (In looking at your filestamp, you might or might not want to show the “Home” link if you are on the home page. It is up to you, and I’ll leave the JavaScript code for checking it up to you as an exercise.) What we can do is check the current page—the one we’re viewing—against the home page, the one we created in the “homePage” string. Add a line to get the parent page’s URL just after you set the homePage string: homePage = new String('/NetLife/'); parentPage = findParent(document.location, homePage);
Advanced JavaScript
24
This calls a subroutine called “findParent()” which will give us the parent URL of the document’s URL. We’ll write that subroutine in just a bit. Change the document.write line that prints the Home link to: document.write(''); if (parentPage) { document.write('Parent | '); } document.write('Home');
We’re going to put the “parent” link on the same line as the “Home” link. So we break out the part where we create the table cell, and if the “parentPage” string has anything in it, we put that on the line in front of “Home”, with a vertical bar to separate the two words. function findParent(childPage, homePage) { childPage = new String(childPage); homeLocation = childPage.indexOf(homePage); if (homeLocation >= 0) { //first, chop off everything in front of the homePage parentPage = childPage.slice(homeLocation); //second, find the location of the final / and chop to it //but first, if the last character is a /, get rid of it if (parentPage.charAt(parentPage.length-1) == "/") { parentPage = parentPage.slice(0, -1); } slashLoc = parentPage.lastIndexOf('/'); if (slashLoc > 0) { parentPage = parentPage.slice(0, slashLoc+1); } else { parentPage = ''; } } else { //the home page is not contained in the child page parentPage = ''; } return parentPage; }
This is probably looking pretty complicated. We haven’t done this sort of thing to strings before. function findParent(childPage, homePage) {
We are creating a function called “findParent()” that takes two arguments: the page we’re trying to find the parent of (we’re calling it “childPage”) and the ultimate parent (the “homePage”). childPage = new String(childPage);
First, we ensure that the “childPage” variable is in fact a string object, because we are going to need to use some serious string functions on it. homeLocation = childPage.indexOf(homePage); if (homeLocation >= 0) { //first, chop off everything in front of the homePage parentPage = childPage.slice(homeLocation);
25
Advanced JavaScript
When you ask for string.indexOf(otherstring), you are looking for the string “otherstring” inside of “string”. This returns a number, from zero to the length of the string, depending on where the “otherstring” is. Actually, the maximum location of “otherstring” is one less than the length of the string, because JavaScript starts counting from zero. The first location in a string is zero, and the last location is the length minus one. If you look for the string “a” in the string “Gila”, “indexOf()” will return 3. If you look for the string “Gi” in the string “Gila”, “indexOf()” will return 0, because that’s the location the string “Gi” starts at in the string “Gila”. So when we ask for the location of “homePage” inside of “childPage”, we know that homePage exists if the result is zero or more (“indexOf()” returns a negative one if the needle does not exist in the haystack). Since URLs are relative, we can make things a little easier on ourselves by chopping off everything except the homePage portion and whatever exists after the homePage portion. That’s what the “slice()” function does. It slices the string, starting at the number we give it. So when we call “slice(homeLocation)”, we throw away everything that occurs before the homePage portion of the string. If the homePage is “/NetLife/” and the childPage is “http://www.hoboes.com/NetLife/Web_Scripting/” then after the slice, parentPage will be “/NetLife/Web_Scripting/”. if (parentPage.charAt(parentPage.length-1) == '/') { parentPage = parentPage.slice(0, -1); }
We are going to find the parent by chopping off the last portion of the URL. URL portions are separated with the “slash” character. But some URLs have a slash at the end, so we need to get rid of that slash if it exists. The “charAt()” function tells us what character is at that location. Since locations begin at zero, the final location is length minus one. Here, we’re checking to see if the final location in “parentPage” contains a slash. If it does, we do the next line, which slices parentPage from 0 (the first location) to negative one (a special number that tells slice to chop one character off from the end). slashLoc = parentPage.lastIndexOf('/'); if (slashLoc > 0) { parentPage = parentPage.slice(0, slashLoc+1); } else { parentPage = ''; }
We set the variable “slashLoc” to the location of the last slash in parentPage. The function “lastIndexOf()” is just like “indexOf()” except that it starts looking from the end of the string instead of from the beginning.
Advanced JavaScript
26
Then we check to see if the slash’s location is greater than zero. If it is zero (or less), then there is no parent page, so we just set parentPage to an empty string. If it is greater than zero, we slice parentPage from the beginning to the location of the slash. When you give “slice()” a second number, “slice()” slices to the character in front of that number’s location. So if we want to actually include that character, we need to add one to the slash’s location. We want our parent to include the final slash, so we do the addition. At this point, if childPage contained, for example, “http://www.hoboes.com/NetLife/Web_Scripting/lessons/” then parentPage now contains “http://www.hoboes.com/NetLife/Web_Scripting/”. The rest of the script just returns this to whoever called the function “findParent()”. You can see an example of this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson11.html.
Adding the parent’s name Rather than simply write “Parent” as the link’s text, we can write the name of the parent’s folder. This is relatively simple compared to grabbing the parent. In fact, it’s somewhat complementary: instead of grabbing everything before the current location, we want only the current location. If our parent is “/NetLife/Web_Scripting/” we want “Web_Scripting”. In the “filestamp()” function, under where we set “parentPage”, add a line to set “parentName”: parentPage = findParent(document.location, homePage); parentName = getName(parentPage);
Change the line that writes the Parent link to: document.write('', parentName, ' | ');
All we need do now is create the “getName()” function. function getName(theURL) { //find the location of the final slash if it exists if (theURL.charAt(theURL.length-1) == '/') { theURL = theURL.slice(0, -1); } slashLoc = theURL.lastIndexOf('/'); if (slashLoc > 0) { theName = theURL.slice(slashLoc+1); //get rid of trailing .xxx dotLoc = theName.lastIndexOf('.'); if (dotLoc > 0) {
27
Advanced JavaScript theName = theName.slice(0, dotLoc); } } else { theName = 'Top'; } return theName; }
Most of this we’ve already seen. We check to see if the last character is a slash, and if so, we delete it. We then check to see where the final remaining slash is, and this time, we slice from that location instead of to that location. We also check to see if there is a period in the resulting name. This will happen if, for example, our name is “fred.html” or “current.cgi”. Usually, we wouldn’t want to see the “.html” or the “.cgi”, those are for the computer. So we chop anything after (and including) the final period. If there is no name (for example, the page URL is “/”), we just return the name “Top”. See this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson12.html.
Bread Crumb Trail Now that we can get the name and the URL for the parent, why not show all the parents, in order? This is like the “trail of bread crumbs” that Hansel and Gretel left to know where they came from. It shows the viewer what pages normally “contain” this page, and thus provide possible avenues of exploration for further information. We already know how to “loop” with the “while” statement, but we didn’t pay much attention to it. Here, we’ll use the “while” statement to keep adding new parents onto the old parent until there is no more parent left. First, change the “if/then” that prints the parent to: if (parents) { document.write(parents); }
We will create a string called “parents” that contains the names and links to the parent pages. Now, we need to create that variable. Up at the top, where you set the parentPage and the parentName, replace those two lines with: parents = ''; parentPage = document.location;
Advanced JavaScript
28
while (parentPage = findParent(parentPage, homePage)) { parentName = getName(parentPage); parentLink = '' + parentName + ''; parents = parentLink + " | " + parents; }
On the first line, we set the “parents” variable to an empty string. We need to do this because, inside the while loop, we are going to be building this string up piece by piece. Each time we get a new parent, we will add the new parent to the pre-existing “parents” variable. But the first time through the loop, there is no pre-existing “parents” variable unless we have already created one. That’s what we do here. We created the “parents” variable, but left it empty. We do the same thing for “parentPage”. We’ll be setting this in the loop, but we’re also using it the first time through the loop. So we need to set it to our starting point before we enter the loop. We set it to the document’s location. Our first parent will be the immediate parent just above our document’s location. while (parentPage = findParent(parentPage, homePage)) {
Our “while” will repeat all of the JavaScript lines between the “{” and the corresponding “}” four lines down. It will continue to repeat those lines until the above function returns nothing, or zero. In this case, it will return nothing, since that’s what our “findParent()” function does when there are no more parents. As long as “parentPage” contains something, the “while” will make JavaScript loop through the next few lines. Remember that “parentPage” is getting smaller each time we call the “findParent()” command. Eventually, it will be empty, and the loop will stop. parentName = getName(parentPage); parentLink = '' + parentName + ''; parents = parentLink + " | " + parents;
For each parent, we get the name of the parent. Then we create the link. And then we “prepend” the link to the previously existing “parents” variable. When the loop is completed, the “parents” variable will contain each of the parents, separated by a vertical bar. See an example of this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson13.html.
Cookies I’m sure you’ve seen web pages that tell you how many people have visited a site. JavaScript can’t do that. But it can tell the visitor how many times their computer has visited the site. Your script can do this by storing a “cookie” on their computer, and then checking that cookie every time it runs. A “cookie” is like a variable that lasts even when the script isn’t running. The script can ask
29
Advanced JavaScript
for it whenever the script starts up, and then modify it as well. We can use a cookie to keep track of how many times the current user has loaded our web page. Cookies, like JavaScript, exist on the client computer. They can be turned off, and they can be modified by the client. Like JavaScript, you cannot trust cookie values. But you can use them to assist the user, to store information such as when they last visited and what their browsing preferences are if you allow a choice of preferences. Try to keep the number of cookies you use down to one or two. If you need more, you probably need to keep a database on the server to store the user’s information. We are going to use our cookie to keep track of how many times the visitor has been to our site. Using cookies comes in two parts: setting the cookie, and getting the cookie. Each cookie has a name, a path, and a value. The path is important. It ensures that we are the only ones who get to see this cookie (other than the client). If, for example, you are using geocities, and you set the path to “/”, you’ll end up giving that cookie information to anyone else at geocities. In your scripts.js file, after setting “changeDate”, add three lines for getting, adding to, and setting your cookie. changeDate = new Date(document.lastModified); visitCount = getCookie('Visits', 0); visitCount++; setCookie('Visits', visitCount, homePage, 365);
There are two functions in there that we have not yet seen. One is “getCookie()” and one is “setCookie()”. We will need to create both of these. We will also need to write this visit information to the web page. Add the following lines to the set of writes you’re doing: document.write(' |
You have visited us ', visitCount, ' time'); if (visitCount != 1) { document.write('s'); } document.write(' |
');
If visitCount is one, we tell the viewer that they have “visited us 1 time”. Otherwise, (for example, they’ve visited us four times) we tell them that they have “visited us 4 times”. We write the “s” in “times” only if visitCount is not 1. Now on to our cookie functions. Our first function will get the cookie. If there is no cookie (which there won’t be, the first time we get it), it returns a pre-specified default value.
Advanced JavaScript
30
//get a cookie function getCookie(cookieName, defaultValue) { //see if the cookie is there already cookieName = new String(cookieName + '='); //get a list of all cookies allcookies = document.cookie; cookieLoc = allcookies.indexOf(cookieName); if (cookieLoc >= 0) { //the viewer has been here before cookieStart = cookieLoc + cookieName.length; cookieEnd = allcookies.indexOf('; ', cookieStart); if (cookieEnd == -1) { cookieEnd = allcookies.length; } cookieValue = allcookies.slice(cookieStart, cookieEnd); cookieValue = unescape(cookieValue); } else { cookieValue = defaultValue; } return cookieValue; } function getCookie(cookieName, defaultValue) {
Our function will take two arguments: the name of the cookie, and the default value if the cookie doesn’t exist yet. //see if the cookie is there already cookieName = new String(cookieName + '=');
JavaScript gets cookies as one big string full of all the cookies (like a cookie jar…). In order to find the cookie we want, we have to look for it. Cookies always begin with the cookie’s name and then an “=” sign. //get a list of all cookies allcookies = document.cookie;
The string that has all the cookies in it can be gotten from “document.cookie”. cookieLoc = allcookies.indexOf(cookieName); if (cookieLoc >= 0) { //the viewer has been here before
We use “indexOf()” to see if the cookieName exists in the cookie jar. If it does (if we get back any number zero or greater), then the cookie exists. cookieStart = cookieLoc + cookieName.length;
The start of the cookie value will be write after the cookie’s name. So we add the length of the cookie’s name to the start of the cookie’s name to find out where the value starts. cookieEnd = allcookies.indexOf(';’, cookieStart);
Cookie values always end with a semicolon, and semicolons cannot be in the cookie’s value, so we can find the end of the cookie by looking for the next semicolon after the start of the cookie value.
31
Advanced JavaScript if (cookieEnd == -1) { cookieEnd = allcookies.length; }
But, oops, there is one case where the cookie value might not end in a semicolon: if it is the last cookie value in the list. Then, the end of the value is the end of the cookie list. cookieValue = allcookies.slice(cookieStart, cookieEnd); cookieValue = unescape(cookieValue);
We use “slice()” to grab the cookie value from start to end, and then we “unescape” the cookie’s value. There are special characters, such as the semicolon, that cannot appear in a cookie’s value. When placed there, they have to be “escaped”. We unescape them with the unescape function. Then we have the cookie’s value, so we return it to whoever called the function. We now are able to get cookies, but we still haven’t set any. //set a cookie that will expire in x days //and is valid on validPath function setCookie(cookieName, cookieValue, validPath, expiration) { //get the current time expirationDate = new Date(); //add the requisite number of days by converting to milliseconds expirationDate = expirationDate.valueOf() + expiration*24*60*60*1000; //turn it back into a date expirationDate = new Date(expirationDate); expires = '; expires=' + expirationDate.toGMTString(); path = '; path=' + validPath; theCookie = cookieName + '=' + escape(cookieValue) + path + expires; document.cookie = theCookie; }
We are doing a little work with dates here as well as with setting cookies. function setCookie(cookieName, cookieValue, validPath, expiration) {
Our function is called “setCookie()” and it takes four arguments: the name of the cookie, the value that the cookie will store, the path on the server where the cookie will be returned (it will be returned for that path and for any lower paths), and the number of days the cookie will last before it expires. //get the current time expirationDate = new Date(); //add the requisite number of days by converting to milliseconds expirationDate = expirationDate.valueOf() + expiration*24*60*60*1000; //turn it back into a date expirationDate = new Date(expirationDate);
We get the current time by asking for a new date. Then, we take the value of the date in milliseconds—that is, the number of milliseconds since the start of time on this computer—and add
Advanced JavaScript
32
a day’s worth of milliseconds for every day we want this cookie to last. We then turn the variable back into a date by asking for a new date again, but this time given it our new number of milliseconds to create the date from. We now have a date that is the correct number of days in the future. As a side note, remember that for this to work, the date on both the server and on the client must be correct. If one is off, cookies will expire before they are supposed to. Dates and times are extremely important to modern computers. Always set yours to a trusted central time server. See your computer’s “Date & Time” control for more information about how to do that automatically. expires = '; expires=' + expirationDate.toGMTString();
The cookie is made up of parts separated by semicolons. Our expiration date will be a semicolon, a space, the word “expires”, an “=” sign, and then the expiration date in universal time. path = "; path=" + validPath;
Similarly, the path is a semicolon, the word “path”, an “=” sign, and the path. theCookie = cookieName + "=" + escape(cookieValue) + path + expires;
We construct the cookie by placing the cookie’s name, the “=” sign, and the escaped cookie value (remember above where we unescaped the value?) in front of the path and the expiration date. document.cookie = theCookie;
Finally, we give the cookie back to the document. You might expect that the above line would erase all current cookies and replace them with our single cookie, but this is a special case. JavaScript takes the cookie we’re giving it and adds it to the list of cookies. If the cookie we’re giving it already exists, it replaces that cookie, but does not erase any other cookies. See an example of this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson14.html. Click reload to see the number increase by one. Remember that you’ll need to have cookies turned on in order to see it! For an additional exercise, try storing the document’s last modification date in a cookie, and then let the viewer know if the document has been modified since they last visited.
33
JavaScript Issues
JavaScript Issues It might not be there Not all browsers have JavaScript. Some non-visual browsers won’t use it. And you cannot rely on JavaScript existing even in visual browsers. Even if the viewer leaves JavaScript turned on in their browser, there’s a very good chance that some functionality will be turned off. Mostly, this is because JavaScript is commonly abused by scammers. Many people have pop-up blocking turned on, for example, so that they don’t get advertisements popping up on their computer; most browsers have this option built-in because the problem is so widespread. Some browsers let the user choose exactly what JavaScript functionality to allow, from the ability to resize windows on. When going over this tutorial in early 2007, I discovered that the very simple first example didn’t function at all in two out of three of the then-major browsers. I’d chosen setting the text in the status bar as the first example because it required only a single line and because its result was so obvious. No longer; most likely the functionality was abused by phishing sites. As far as I can tell, the ability to see changed status text is no longer available in most browsers. Browsers will also attempt to detect whether your script is consuming too many resources on the client computer. If the browser detects this, it will either automatically stop your script, or present the viewer with a dire warning that the script is likely doing something wrong, and do they want to cancel? Most likely they will.
Trusting JavaScript You cannot trust JavaScript at all. Not only can the viewer turn JavaScript off, but they can also change it. A JavaScript file is just like a web page: it gets downloaded to the client computer and then “displayed”. The viewer has full control over the file since it is on their computer, and malicious users can take advantage of this if you trust the “output” of JavaScript.
JavaScript Issues
34
JavaScript should be used to assist the user, not to assist the server (or the webmaster). It should not be used to perform authentication, nor should it be used to calculate things that are then stored on the server. You would never want to calculate prices using JavaScript and then charge a customer based on that calculation, for example: you’ll end up giving some people a pretty hefty discount when they realize they can manipulate the calculation themselves. This does not mean that you cannot use JavaScript to show the viewer a quick summary of their prices or shopping cart total. It just means that you should, once they submit their purchase, calculate the prices on your end as well. Remember that on the Internet, it only takes one person to take advantage of security flaws in your web site. JavaScript is a great feature for making your web pages easier to use. But it should not be used in place of server-side programming. Your pages should always be accessible to people who do not use JavaScript (it is difficult to imagine what a JavaScript would look like through a translator or a voice reader, for example), and you should not rely on the JavaScript calculations coming to your end intact and trustworthy.
Different Versions Different web browsers, and different versions of web browsers, will often treat the same JavaScript slightly differently. This will be more of an issue the more complex your JavaScript becomes. Try to keep your JavaScript standard, try to test on a wide variety of browsers and versions, and make sure your web pages work if your viewers end up having to turn JavaScript off to bypass inadvertent bugs in your JavaScript. Remember that just because your code works now, there is no guarantee that it will work with tomorrow’s browsers. Keep things simple, and especially try not to “take advantage” of bugs in a browser’s JavaScript implementation. Someday that bug will probably get fixed.
Managing Your Script Files If you look at other people’s web pages, you’ll notice that sometimes they’ll put JavaScript right inside their web pages, instead of putting it into a separate file as we’ve done here. I recommend against this. By putting your JavaScript into a separate JavaScript (.js) file, you can call the same file from different web pages. If a new browser version necessitates changing your JavaScript code to overcome a new bug, you’ll be able to make the change once, instead of looking for all the web pages you’ve used something like that code in. You’ll always miss at least one.
35
JavaScript Issues
Separating your JavaScript into its own file also makes it much harder for your JavaScript to interfere with your HTML in browsers that don’t implement JavaScript, or don’t implement it the way you expect it to work. You can put more than one <script src="…"> in your web pages, to reference more than one set of JavaScripts. Just make sure your JavaScript functions all have unique names.
Programming Tips
36
Programming Tips Describe Your Goal Write down what you wish to accomplish. Then break it down into smaller parts. Try to find the simplest part, and implement just that part. If you can’t get that simple part to work, see if you can break it down into even simpler parts. If you can’t break it down any further, move on to the next problem. Perhaps by the time you finish the problem you can solve, you’ll have some insight into the related problem that you couldn’t.
Test Often Learn to love your browser’s reload button. Whenever you make a change that might affect your web page, test immediately. Don’t make a whole bunch of changes before testing; if your web page no longer works, you won’t necessarily know which change caused it to fail. Write your programs a step at a time and test often. Test things that you don’t even expect to happen: try to think up weird scenarios and come up with a means of testing what happens to your page under those scenarios. This will give you valuable information about what your code is really doing. Also, make extensive use of the “window.alert()” function to let you know the value of variables at troublesome points.
Centralize Your Code You should almost never repeat the same basic code more than once. If you need the same routine a second time, seriously consider moving it into a subroutine. For example, if you wanted to put the date into your “Current Time:” line, we already have the code that creates a date. Unfortunately, if you call it directly you’ll end up with “Today” as the string, because that’s what that subroutine does. Rather than duplicating the portion of the code that creates the actual date, move it out of the “formatDate()” subroutine. Call it something like
37
Programming Tips
“makeDate()”. Then, you can call that subroutine both from “formateTime()” and from “formatDate()”. Another example might be the code we used for prepending a zero to numbers that are smaller than 10. We probably should have created a subroutine that would do that for us, and then call that subroutine every time we need to prepend zeros. But consider the things you might want to do with a subroutine at the moment you create it. Might we want to prepend something other than zeros? Might we want to pad to three or even four characters instead of just to two? An appropriate subroutine might be called with “thishour = leftpad(thishour, "0", 2);” to ensure that “thishour” has at least two characters, and prepend a zero if it does not. One could then use “indexline = leftpad(pagenumber, ".", 4);” to prepend periods and ensure four characters in the “indexline” string. The function might look something like: function leftpad(theItem, thePad, minSize) { newItem = new String(theItem); while (newItem.length < minSize) { newItem = thePad + newItem; } return newItem; }
Avoid the Hammer Syndrome Once you have a hammer, all problems start to look like nails. It can be easy, once you start programming JavaScript, to look at every web page problem as a problem with a JavaScript solution. Look for simpler HTML solutions first. HTML is faster and more reliable than JavaScript, and will last longer. Some problems will have to be solved with server-side programming, either using server-side includes or server-side programming such as with Perl or PHP. Trying to solve these problems with client-side JavaScript is prone to errors, sometimes dangerous ones if money or privacy is involved.
Programming Tips
38
More Information The two most useful books I’ve seen for JavaScript webmasters are Webmaster in a Nutshell, which is simply a useful reference for all sorts of webmaster information, and JavaScript: The Definitive Guide (http://www.hoboes.com/Books/javascript.shtml), by David Flanagan. You’ll find links to both of these on my Recommended Software page at http://www.hoboes.com/NetLife/NSpace/software.shtml. Thanks go to Joy Brunetti (http://www.sandiego.edu/~brunetti/) for testing out the sample scripts and being the first guinea pig! “The best book on programming for the layman is Alice in Wonderland; but that’s because it’s the best book on anything for the layman.”
39
GNU Free Documentation License
GNU Free Documentation License Version 1.1, March 2000 Copyright (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
0. Preamble The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
1. Applicability and Definitions This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque". Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
GNU Free Documentation License
40
The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
2. Verbatim Copying You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies.
3. Copying in Quantity If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
4. Modifications You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: 1.
2. 3. 4. 5. 6. 7.
Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five). State on the Title page the name of the publisher of the Modified Version, as the publisher. Preserve all the copyright notices of the Document. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
41 8. 9.
10.
11. 12. 13. 14.
GNU Free Documentation License Include an unaltered copy of this License. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
5. Combining Documents You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
6. Collections of Documents You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
GNU Free Documentation License
42
7. Aggregation with Independent Works A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
8. Translation Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
9. Termination You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
10. Future Revisions of this License The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
Related Documents