Author: Dmitry Nizhegorodov
Javascript code goes inside tags <script> </script>.
To start with, create an html page, and add to it the follwoing:
It does not matter if you put it inside HTML header or body.
Nothing visible happens when you load your page, but code is executed! To see some useful results, you need to print something. In javascript, you can print using this:
document.write(....)
for example, you can print the values of your variables:
Syntax of javascript is a lot like C or java - with ';', '{' and '}', 'if', 'for', etc:
but variable declarations require no types (like "int x"), only "var", which simplifies programming.
If you want to merge together 2 string values in javascript, you use '+', much like in java:
this will print text
x = 3
into the browser.
this will print "x = 3"
You can merge as many strings or string-values as you want:
x = 3, guaranteed!
So far, our printing is very plain. However, because we print directly into the browser, we can use HTML tags in our prints! Example:
or even
Note that instead of one big document.write() we can use several in a row:
with the same result.
We can now apply the same idea to print x and its value. To do this, we'll use + to concatenate together the html tags, string "x" and the value of x:
Similarly, we can generate a report formatted as an HTML table when we print values in our for-loop. We need to print <table> once, hence we do it outsed of the loop, then we should use "<tr><td>" + i + "</td></tr>" to print i inside of our loop, and then finish with printing </table> outside of the loop:
We can even use the value of variable i in our for-loop to generates H tags of different sizes while loop executes! Consider string
"<hN>"
Here, if we can substitute N with the value of i, we will produce "<h1>", "<h2>", ...
We can do this easily using operator +
"<h" + i + ">"
this indeed will produce "<h1>", "<h2>", ...
Now we can use it in our script to print each value with a separate size!
can be turned into a function this way:
where printX is the name we decided to give to it.
Now our page does not print anything!!! This is correct. It is because we changed the script that is executed by the browser immediately, to a function definition which is remembered by the browser, but not executed. To see the function to print "x = 3" we need to call it. "Calling" a function means using its name in an expression. When function is called, its name is used with ():
Here, we first defined the function printX, and then called it. Only when we called it, its body was executed, and not when we defined it. Note that the body is executed each time a function is called, does no matter how many times:
Any script can be converted to a function. More, a function can be made to return a value back to whoever called it, so that it can be used by other functions:
This makes functions to be much more useful then scripts. Also, functions can also take values in, assigning them to their own (local) variables. Such variables are called parameters and are specified in those () that we write after the function name in its definition:
function sayHello (toWhom) { return "Hello, " + toWhom; }here, toWhom is local variable. Its value is set when it is called. When any function with a paramter is called, teh caller passes the value for the paramters in those () that follw function name in the function call expression:
document.write(sayHello("world"));
this will print "Hello, world"
functions can do more than only string concatenation. They can do everything! For example, convert kilograms to pounds:
function kilogramsToPounds (kg) { return kg * 2.16; }Now let us generate a table showing convertion of some useful weights from kgs to lbs:
Note that expression
kilogramsToPounds(weight)
is in all respects equivalent to expression
weight * 2.16
So one can ask: why I need functions? The answer is that functions help to "factor out" code that is executed many times and in many places, so that you do not need to cut&paste and replicate it in many places!
Consider how we build HTML-tagged texts in our previous lesson:
"<h1> Hello </h1>"
THat's already too tedious to write, but consider
"<td>" + x + "</td>"
Wouldnt't be better to write instead
h1("Hello")
or
td(x)
??? Clearly, better!
We can do that if we create corresponding functions h1 and td. It is very easy!
function h1 (text) { return "<h1>" + text + "</h1>"; } function td (text) { return "<td>" + text + "</td>"; }Let's continue on with h2 h3 h4..., tr, th:
function h2 (text) { return "<h2>" + text + "</h2>"; } function h3 (text) { return "<h3>" + text + "</h3>"; } function h4 (text) { return "<h4>" + text + "</h4>"; } function h5 (text) { return "<h5>" + text + "</h5>"; } function h6 (text) { return "<h6>" + text + "</h6>"; } function tr (text) { return "<tr>" + text + "</tr>"; } function th (text) { return "<th>" + text + "</th>"; }We now have so many useful functions it is impractical to cut&paste them to each page where we want to use them. Javascript offers an alternative - put them all in a file and refer to such file by its name from any page where we want to use teh file's content. This is done using 'src attribute of html tag script:
I recommend to put our HTML functions in file html.js:
html.js |
function h1 (text) { return "<h1>" + text + "</h1>"; } function td (text) { return "<td>" + text + "</td>"; } function h2 (text) { return "<h2>" + text + "</h2>"; } function h3 (text) { return "<h3>" + text + "</h3>"; } function h4 (text) { return "<h4>" + text + "</h4>"; } function h5 (text) { return "<h5>" + text + "</h5>"; } function h6 (text) { return "<h6>" + text + "</h6>"; } function tr (text) { return "<tr>" + text + "</tr>"; } function th (text) { return "<th>" + text + "</th>"; } |
Each time you use h1, h2, etc on a page, you can include
<script src="html.js"></script>However, for the examples in this course we'll not use src=... and will continue explicitly including each function in each example. This will allow freely re-defining them from one lesson to another, and alos it will make it easy for yo to experiment with each example completely independently bu changing code as many times as you want. Thi is a "sandbox" approach which we think is very useful because it makes experimentation fun. There are also technical reasons for avoiding src= in a web course liek this one - browser do not liek re-loading .js files, browser are confused when instructed to read from a .js file in a pop-up window. etc.
OK, now let's move on. Here is the same kilogramsToPounds(), but using the html helper functions
This way it is more compact and understandable.
Exercise: Armed with these handy tools, rewrite our code from lesson2
function h1 (text) { return "<h1>" + text + "</h1>"; } function h2 (text) { return "<h2>" + text + "</h2>"; } function h3 (text) { return "<h3>" + text + "</h3>"; } function h4 (text) { return "<h4>" + text + "</h4>"; } function h5 (text) { return "<h5>" + text + "</h5>"; } function h6 (text) { return "<h6>" + text + "</h6>"; } function td (text) { return "<td>" + text + "</td>"; } function tr (text) { return "<tr>" + text + "</tr>"; } function th (text) { return "<th>" + text + "</th>"; }Qestion: what these little functions all have in common?? They all seem to be doing the same operation:
"<tag>" + text + "</tag>"
Only the text and the tag differ - it is teh tag "h1" for function h1, "td" for function td,etc, and the text is inside.
We therefore can redefine all these functions by means of yet another function that takes a tag, a text and creates
"<" + tag + ">" + text + "</" + tag+ ">"
Such function can be very handy, snd writing it is easy - we already know how to pass arguments, how to glue strings and how to return a result. Here it is:
function make (tag, text) { return "<" + tag + ">" + text + "</" + tag+ ">"; }And here we go with our impoved definitions, rewritten to use function make:
function h1 (text) { return make("h1", text); } function h2 (text) { return make("h2", text); } function h3 (text) { return make("h3", text); } function h4 (text) { return make("h3", text); } function h5 (text) { return make("h5", text); } function h6 (text) { return make("h6", text); } function tr (text) { return make("tr", text); } function th (text) { return make("th", text); } function td (text) { return make("td", text); }Note that for any tag foo for which we do not have a special "constructor" function such as h1, ha2, etc., , we can easily get away with just function make:
make("foo", ...)
which is not as clean as
foo(....)
but still much cleaner than writing
"<foo>" + .... + "</foo>".
each time we need a new tag.
Note that we do not discuss tag attributes at the moment pretending we do not need them. We'll cover this a little bit later.
Now we can generate table cells of different sizes. We used the follwing to generate each row in a table:
document.write(tr(td(weight) + td(kilogramsToPounds(weight))));now, assuming 'size' is an variable, we can wrap each value in a pair of Heading tags this way:
document.write(tr(td(make("h" + size, weight)) + td(make("h" + size, kilogramsToPounds(weight)))));
Or even better, let's define a new function h:
function h (size, text) { return make("h" + size, text); }Note how the heading is constructed as "h" + size and passed to function make. Here function h in action:
document.write(tr(td(h(size, "I'm heading of size " + size))));Here is the a complete script:
The conclusion we make from this lesson is that functions are useful because they let us write cleaner and more compact code.
Speaking of cleannes, lets us again review our code. Why we did not use make() to generate <table> tags?
We could use something like make("table, ...) but how to get the text for the entire table passed into it? We can achieve that if instead of printing out each row individually with document.write()
document.write(tr(td(h(size, "I'm heading of size " + size))));
we save each row as a string value, appending it to all the previously generated rows. Javascript allows this with operator +=. For example,
var x = "Hello, "; x += "world"; document.write(x);produces
Hello, world
Note that += is really a "syntax sugar" operator. Any expression that starts this way:
x += ....is just a handy abbreviation for
x = x + ....Nevertheles, += is convenient because it saves typing and clarifies what is going on with the variable.
Armed with +=, let's rewrite our code. We'll collect all tr-rows in a variable named rows and then use make("table", rows) to print the entire table out in "one shot":
This is great, except that our table does not any more have border=1 attribute. That's right, our function make() does not know how to add attributes to html tags. We can impove this by adding a 3rd parameter, 'attributes', to function make, so that it can splice-in a string containg attribute=value pairs:
function make (tag, text, attributes) { return "<" + tag + " " + attributes + ">" + text + "</" + tag+ ">"; }But what is we need no attributes? Do Ineed each time call make this way:
make("sometag", "sometext" "")where the last argument is an empty string?
A great feature of javascript is that it allows to call a function that specifies N parameters with any number of arguments. Thus, the function make
can be called with 2 arguments, kust liek we called it in the previous examples, in which case the parameter attributes
receives a "default" value.
Each language decides what is a default. In Javascript it is null
.
Since a default value for any unsupplied parameter in javascript is value null
, we can detect that in the code. We want to reset it to our own default value, which is usefult to chose as an empty string:
function make (tag, text, attributes) { if (attributes == null) attributes = ""; return "<" + tag + " " + attributes + ">" + text + "</" + tag+ ">"; }Let us now introduce a boolean opearator '||'. Its meaning is "or"" x ||y means x or y. What is great about || in javascript, lisp and a few other hyghly dynamic languages is that the || operator is not looking at its second argument when the first one is non null!.
result = some_non_null_value || never_call_this_func()this may not be easy for a beginner, but with time youu'll appreciate this feature. For our purpose, it lets to re-define make() in this way:
function make (tag, text, attributes) { attributes = attributes || ""; return "<" + tag + " " + attributes + ">" + text + "</" + tag+ ">"; }what we see on the first line on function body is one of javascript's programming idioms: when a parameter is optional, and
null
is not the desired default value, use this:
parameter = parameter || desired_default_valueA concrete example of this you just saw above, where
desired_default_value
was "". Now the last line in our table-building code becomes
document.write(make("table", rows, "border=1"));Please edit the last example, cut &pasting new code from text into the example window to see how this all works.
The functions we've created can be used to generate complex, onion-like - nested - trees of HTML nodes. let's experiment with a new function box
defined this way (try to guess what it is for):
function box (text) { return make("table", tr(td(text)), "border=1"); }Yes, it creates a small bordered table around 'text'. Let's add it to file html.js. Coupled with the += operator, function box can do funny things for us:
Try to understand what it produces and why! And here is a fun variant of box:
Now we'll continue with examples dealing wuth tag attributes.
Immediately we see a problem - specyfying the attribute pairs is too difficult and cumbersome. Too many double-quotes, pluses, equal-signs just to pass in a few attributes. It would be nice to add a function that can generate a string that sets an attribute 'attribute' to value 'value' this way:
attribute='value'
This is easy to do - let's call such function 'attr':
function attr (name, value) { return " " + name + "='" + value + "' "; }
Note we also put double-quotes around value - something that is so tedious to do by hand!
Here's how we can use attr:
We can add a couple of functions to generate tables of few predefined formats:
/* * table0: generate table with no border, no alignment */ function table0 (text) { return make("table", text); } /* * table1: generate table with border=1, centered */ function table1c (text) { var attributes = attr("border", 1) + attr("align","center"); return make("table", text, attributes); } /* * table: generate table with border=1, centered, wide. * This is our defalt table format. */ function grid (text) { var attributes = attr("border", 1) + attr("align", "center") + attr("width", "100%"); return make("table", text, attributes); }here is our complete function list so far.
html.js |
/* -*- Mode: java -*-*/ function make (tag, text, attributes) { attributes = attributes || ""; return "<" + tag + " " + attributes + ">" + text + "</" + tag+ ">"; } function h1 (text) { return make("h1", text); } function h2 (text) { return make("h2", text); } function h3 (text) { return make("h3", text); } function h4 (text) { return make("h3", text); } function h5 (text) { return make("h5", text); } function h6 (text) { return make("h6", text); } function tr (text) { return make("tr", text); } function th (text) { return make("th", text); } function td (text) { return make("td", text); } function h (size, text) { return make("h" + size, text); } function box (text) { return make("table", tr(td(text)), "border=1"); } function attr (attribute, value) { return " " + attribute + "=\"" + value + "\" "; } /* * table0: generate table with no border, no alignment */ function table0 (text) { return make("table", text); } /* * table1: generate table with border=1, centered */ function table1 (text) { var attributes = attr("border", 1) + attr("align","center"); return make("table", text, attributes); } /* * table: generate table with border=1, centered, wide */ function table (text) { var attributes = attr("border", 1) + attr("align", "center") + attr("width", "100%"); return make("table", text, attributes); } |
document.write("Hello, world")
it generates
Hello, world
Code that produces results that are "bigger in size than the code itself" frequently uses iteration. For example, we used iteration to generate a table of kilograms-To-Pounds conversion:
var rows = tr(th("kg") + th("lbs")); for (weight = 40; weight < 100; weight += 10) { rows += tr(td(weight) + td(kilogramsToPounds(weight))); } document.write(table(rows));Here, the action performed by the for loop is iteration. We cold have, but did not do this:
var rows = tr(th("kg") + th("lbs")); rows += tr(td(40) + td(kilogramsToPounds(40))); rows += tr(td(50) + td(kilogramsToPounds(50))); rows += tr(td(60) + td(kilogramsToPounds(60))); rows += tr(td(70) + td(kilogramsToPounds(70))); .... .... } document.write(table(rows));because it is bulky and boring. Instead, the for-loop helped us to save code size and make it more generic.
Iteration is essentially a repetition with small variances within some boundaries. Thus, in the above example the variance consists in sweeping the value of variable weight from 40 to (by`ut excluding) 100 with increments equal to 10: 40, 50, 60, 70, 80, 90.
Iteration is not the only method to achieve similar effect. Recursion is another one. Unlike iteration, that requires for-loops, recursion can be accomplished only by means of functions and operators "if". Unlike iteration, which can be described as "do this and this starting from such-and-such until such-and such", recursion can be described as: "unless your case is very simple - such-and-such - in which case just return this-or-that, reduce what you want to do to a simpler case and do it, and then build the result upon it".
Example:
How to climb a tree: If you see no branches in front of you, stop. You're on the top!. Alternatively, choose a branch that is bigger and closer to the center of the tree, and climb on it, then climb the tree
If you noticed, "climb the tree" refers to itself. This is called recursion.
Here is an example of numeric recursion:
Function sum computes a sum of integers from 0 to number. To learn how it works, let's inspect it from the simplest cases.
Great many computations are naturally described in terms of iteration, but many are naturally expressed as recursion.
Consider function factorial which computes a multiplication of all integers from 1 to a given number:
Very similar to function sum! In fact, most recursive definitions follow the same code pattern with an if-else branches that both return, but one branch recurses, while the other not. The non-recursive branch is called terminator. IT is sometimes cleaner to use javascript conditional expression instead of if-else statement in recursive functions. Compare the following with the previous definition:
Here the ?-branch is a terminator, and the :-branch goes into recusrive computation.
Having said that, let's apply recursion to a a non-numeric domain.
Our first example is function 'boxes' that we introduced in lesson4. To recap:
function boxes (count, text) { for (i = 0; i < count; i++) { text = box(text) ; } return text; }Consider now its recusrive variant:
Here is how it works, assuming our value for text is "hello"
Thus, recusrion can be frequently traced by "unfolding" it, in which case it results in some complex expression, such as box(box(box("hello"))). where the size of the expression is proportional to the "depth" of recursion.
If you feel dizzy, thinking about recursion, it is normal. you can skip the next example, and that's teh end of this lesson for you.
If not, then here is a challenge - how to reverse a string? For example, how to convert "hello" to "olleh" usinmg recursion?
As with any recursive algorithm, the right solution sometimes "flicks" immediately after you recognize how to solve a more complex case using a more simple case. For our challenge, we can discover that to reverse any string we need to reverse its substring from 2nd character to the end, and then append the first character to the result of that. Let's assume we have function tail(s) that returns a substring of string s without the heading character, and thatwe have another function, head(s), that returns a little one-letter string containg just the heading character:
reverse(string) = reverse(tail(string)) + head(string)
our recursive definition is almost done! Howover, we need the terminator. In our case, it is clear that if string contains just one character, its tail is null, and reverse() of such string is just string iself:
reverse("o") = "o"
what else, right? Thus, we can finally write our definition:
function reverse (string) { return (string.length == 0) ? string : reverse(tail(string)) + head(string); }Let's trace how "hello" will be reversed:
reverse("hello") = reverse(tail("hello")) + head("hello") = reverse("ello") + "h" = reverse(tail("ello")) + head("ello") + "h" = reverse("llo") + "e" + "h" = reverse(tail("llo")) + head("llo") + "e" + "h" = reverse("lo") + "l" + "e" + "h" = reverse(tail("lo")) + head("lo") + "l" + "e" + "h" = reverse("o") + "l" + "l" + "e" + "h" = "o" + "l" + "l" + "e" + "h" = "olleh"It's remarkable how this compuation is derived from a simple formula such as
(string.length == 0) ? string : reverse(tail(string)) + head(string)
In this derivation is the essence and the power of recursion.
Note that we can not yet try our example in a browser because javascript does not provide functions head and tail. However, it provides method slice (more on javascript methods in next lessons) which we can use to write our own definitions for head and tail:
function head (string) { return string.slice(0, 1): } function tail (string) { return string.slice(1, string.length): }
Here is the whole example:
A programming object is a container located in computer RAM that is a collection of named variables and functions. Object's own variables are called properties. Object's own functions are called methods.
Many programming objects reflect real-world objects and thus are designed to express some characteristics and behavior.
For example, consider a real-world kitchen toaster. It is a physical object. It has weight, size, certain electric power. It has a power cord. It needs that many volts to operate. It contains knobs to set its temperature and cooking regime. Some toasters have folding doors and a horisontal grill, other have 2 slots for toasts, levels attached to springs that pop up toasts. When turned on, or when a lever is pressed, a toaster starts heating.
Similarly, a programming object "toaster" can have paramteres size, weight, voltage, regime. Programming objects "toaster" can not cook real toasts but they can apply their behavior to programming objects "toast", if they were programmed in such way. And just like real-world toasts turn dark and become hot when toasted, programming objects toasts can change their parametrs "color" and temperature" after being toasted in programming toasters. Thus programming objects can simulate real-world situations and processes.
Yet there is a large category of programming objects that do not simulate, but act as genuine objects representing some programming artifacts. For instance, programming object display-window can act by displaying characters and images on computer screen.
Javascript predefines several objects that act just like that. Among tem, two are used most frequently, and javascript provides global variables to access them - window
and document
.
These objects and variables are available when you execute javascript in a browser. Each browser's window is a separate window object that has its own properties (object's own variables). Each window object has property document which holds object document that represents the html document the window currently displays, or null if it does not display anything. Do not confuse window objects and window variable. Javascript provides you with window variable that holds the current window object.
Thus, object stored in variable window refers to the window that it is currently corresponding to the html page where the script is executed, variable document holds its corresponding document object. The same object can be accessed via
window.document
property. Note that this is just a convention used in javascript, that the name of window's property is teh same as the name of the variable.
In previous lessons we used document.write() to print values out. Precisely, document.write(...) means that we invoke function write that is owned by document and is stored in its property write. Such ownership, both for plain properties and properties-functions, is expressed in javascript using a dot notation.
Each object has a concrete set of properties. Propeties can be retrieved, and some can be modified. Examples:
These are some of the properties of a document.
Creating new object. Javascript offers a way to create new window objects. You can use window.open method to open a new window. Example:
Here the first argument is URL and the second is title. You can also open a local page:
var w = window.open("../", "test");
after the window is opened you can modify its properties:
Please note that this works, according to JAVASCRIPT's "model", only for your current window or for a window which URL is your "domain", such as your local path. You can not get properties of a window which is opened with URL="http://www.yahoo.com". Javascript restricts this because of sequrity reasons, but most people do not understand why.
You can learn about standard objects and their properties at developer.netscape.com
Here we list only few of the properties.
name
attribute. Here we give one example. Suppose, we have a textbox on our page: <input type="text" name="greet_me" size=15>
which contents we want to access and alter from Javascript. The name of the object is greet_me and the name of its property that represents dysplayed text is value
. Thus, teh following prints "Hello"
and the following allows to inspect document's property:
x[3]
retrieves 4-th element of array x. Why 4-th, not 3-rd? because in javascript, like in many languages, arrays are 0-indexed. The first element of each array has index 0, the second - index 1, the thired - index 3, and the fourth - index 3. Nth element has index n-1. This helps to iterate arrays usiung for(var _ =0; _ < max; _ ++) loops:
for (var i = 0; i < max; i++) { document.write(x[i]); }
When [] used on left-hand-side of an assignment operator =, an element of an array is stored:
x[3] = "hello";
x[4] = 777;
In javascript, unlike many other languages, arrays are true objects. An array has properties and can have methods. MOre important, array elements are its properties, and indices are corresponding property names! This makes arrays and objects closely related, and that is why objects also use [] notation to access properties by-expression. In, fact, the mechanism used by arrays and objects is fundamentally similar, since in javascript all arrays are objects, but all objects, to some extent, are implemented as arrays.
An object of type array differes from a regular object in that it has one specific property, 'length', describing the number of elements array has. Thus, one can iterate all elements as simply as:
for (var i = 0; i < x.length; i++) { document.write(x[i]); }
In javascript, arrays are dynamic. This means an array's length grows when more values are added to the array. Suppose, you have an array of length 5 and then you set its 1000th element:
x[1000] = "tischa"
then x.length becomes 1001.
Creating new arrays is easy using [] notation:
var arr = [ "one", two" ];
here, we created an array of length 2 and stored it into variable arr. The expression with braces is called array initializer. Expression
[]
creates an empty array.
Arrays are handy when you want to keep track of some values for each day, week, month, etc. For instance, consider an array holding some dollar amounts for past 12 month:
[ 26, 12, 34, 40, 31, 56, 79, 60, 87, 110, 102, 115 ]
We now can buid a bar-chart of these amounts. For doing that, we can display a little gif image that we can each time extends to a proper hight representing the price. Lte's reuse the technigue of resizing an image
Just to recap, objects in programming languages are much like real-world objects. They occupy some space (resources), they are stable, have attributes and behavior. If this reminds real-life objects like books, pens, pencils, That's what programming objects were invented for.
Typically an object is created using a contructor function - a function that creates a fresh object and returns it. Some contructor functions take arguments, in which case these arguments used to create an object and set some of it attributes.
Consider a built-in constructor Date(). It creates a new object that represents a current date.
select * from DEPT; DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS ... select * from EMP; EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- --------- ---------- ---------- ---------- 7369 SMITH CLERK 7902 17-DEC-80 800 20 ....These are nice, and for mny,convenient, tables, RDBMS table rows are artifacts that model real-world entities in a rather crude way. Compare with these:
var dept = new Department(); dept.deptno = 10; dept.name = "Accounting"; dept.location = "New York"; var emp = new Employee(); emp.empno = 7369; emp.name = "Smith"; emp.job = "clerk"; emp.manager = 7902; emp.hireDate = new Date("17-DEC-80"); emp.salary = 800; emp.department = dept
What is a rollover script? If you have seen a Web page that changes its look while you move your mouse across it ("roll it over", hence the catchy name "rollover script"), it's probably using a variant of a rollover script (if it is not, then it is using CSS). Somebody mentioned that people adore this sort of actions on web pages and I tend to agree that it is nice if done in modereation.
No wonder there are myriads of pages with rollover scripts. Every author uses a different variation, but all rollover scripts work similarly, more or less. It's not an oversimplification to say that there is just one canonical rollover script. Rollovers are extremely widespread - 9 out of 10 javascript-powered sites do a form of rollover. Thus, javascript rollover is a mother of all javascript scripts, a silver bullet, a killer app.
Pretty much every rollover javascript code uses images that are placed inside of <A></A> tags. That's because <A> tag tracks mouse movement events onMouseOver and onMouseOut. What does it mean?
Each GUI application that is capable of responding to mouse moves is using user interface events. Some GUI frameworks are so much event-oriented that they are officially called "event-driven". You do not need at this point to understand all the gory details of event-driven application models and the javascript event handling model. Just assume that the javascript engine of your browser tracks every move of your mouse and immediately notices when the mouse is crossing boundaries of html A elements on the screen. For example, picture that you have an A HREF region and an image inside of it:
<a href='...'> <image ...></a>
Javascript engine notices when the mouse cursor steps onto the image and when it steps out (note that stepping in and stepping out is not the same as mouse click, and happens when you just move the mouse across the screen without clicking its buttons).
When javascript detects that the mouse crosses the boundaries of the A link (which covers the whole image), it responds by generating events. These events, like other javascript events are javascript objects. They describe what happens. Thus, if the mouse is stepping onto the image surrounded with the link tag, javascript will generate event "mouseover", specifying in the event properties exactly which tag was crossed, and other details such as coordinates, etc. Then, the javascript engine looks in its internal tables for an event handler for this event.
An event handler is a javascript script that should be executed when an matching events happens. If javascript runtime engine finds a suitable handler, it executes it, otherwise the event is discarded as non-used.
An HTML writer can specify event handlers associated with any A region. To create an event handler, you need to add a special attribute, an event attribute, to the tag. There are several commonly-used event attributes: onClick, onMouseOver onMouseOut. The value of any event attribute is event handler - javascript code to run when the event happens. For example, consider
here the background color of the page will change to black when the mouse moves over text "Moscow News paper", and changes back to its original color when the mouse moves out.
What we wrote is not *the* rollover script yet. Most rollover scripts replace the picture displayed by an IMAGE tag when the mouse crosses an HREF. How they do it? Suppose we have 2 gif images, button.gif and button_glow.gif, and we would like the button to glow when the mouse crosses over. Here is the start:
<a href="http://www.google.com" onMouseOver="" onMouseOut=""> <image src="fill.gif"> </a>The question is: what actions to perform for onMouseOver and onMouseOut. It turns out that most javascript-enabled browsers support property document.images which is an object holding a separate property for each image tag on the page that has 'name' attribute. Thus, if we specify
<image name="drawing" src="fill.gif">
then document.images.moscow will hold an object representing the image. It also turns out (see developer.netscape.com/docs/manuals/js) that any such image object supports property src, and by altering this property by means of javascript code you can force browser to load and display a different image. Now we have all the ingredients for the solution:
In javascript, you can access and modify the source GIF or JPG that the tag displays. If you modify the source, the picture will change instantly!
Now, that's the complete rollover script! In fact, there is no *the* rollover script but there is something called a code PATTERN, after Christofer Alexander's "Quality Without a Name" and "The Pattern Language". The rollover pattern consists of
In one of the next lessons we'll perform a comparative case-study of several widely used (but necessarily good!) implementations.
<INPUT TYPE="button" VALUE="Black" NAME="setBlack" onClick="document.bgColor='black'">
Here we execute a javascript expression that sets document's background color to black. Note that we had to use '' quotes since we used "" around the value of the onClick attribute. Sone web programmers skip the quotes for HTML attribute values wherever possible - do not do that! It's not a bad style, it is just illegal in modern HTML/XML and is only possible because browsers are prepared for this sort of abuse.
we can place several buttons on a page, each changing its background to a different color:
This is great except the code is a bit repetitive - each INPUT's onClick contains document.bgColor=.. We can use a helper function instead:
This represents a little bit better design, but not perfect. First of all, it still leads to errors - look how be have 3 buttons with VALUE equal Black, and two that set color to Black. This is the result of a mistake in a cut-and-paste, teh most common source of lots of bugs. In this particular code pattern the osurce of the bug is that we have to type each color twice - one for button value and one for onClick. We can improve this situation fundamentally if we decide to generate the HTML code for the buttons:
Another variant of the same idea - to use an array of colors:
Here we've learned how to generate collections of buttons with code attached to them. Groups of buttons performing UI actions with assotiated code sometimes called controllers. Thus, we've learned to automatically generate controllers.
Let's now reflect a little bit upon our html.js functions. We think that it is great to have various function to build elements of HTML trees instead of fighting each time with lots of strings with html tags, etc. By doin that, we moved away from boring, uneducated cut&paste, temlate-like style of writing script to more advanced an elegant functional style. TemplateServletlates are for birds - they are for the part of your brain that recognizes visual patterns, not thinks. Consider how we use one of our basic functions - h1:
h1("hello")
Wait - this looks awfully like a constructor. Indeed, it is, except what we create is a string. Here, we create an html node h1 containing text "Hello". If an html node is an object then our h1 returns its "printed representation". Objects are better because you can combine objects, recombine them, and finally produce printed representation. Say, I have one h1 html node, I have two P nodes and one TABLE node, I wan to combine them in such-and such way.
It would be useful, having created a couple if TableRow objects to be able to add them to a Table object, and later print this table out to a document:
var table = new Table() ... table.add(row1); ... table.add(row2); table.print(out); // or, at least document.write(table.toString())How to do this? Let's consider constructor Date again. Date creates a new object, a date, and returns. A Date object carries a method, toString(), that let it to be printed to document.write in a form suitable for dates - year, month, date, etc. Let us define a constructor function that creates an object that will be an html node. The argument to such constructor function will be a string definig its tag, saved as object's property. there also will be method toString that will print the tags and the text of the node:
function Node (tag) { this.tag = tag; }To create a new object using this constructor, we use operator 'new':
new Node(...)
for example:
var h1_node = new Node("h1") var td_node = new Node("td");We can construct any number of nodes this was.
This is fine except teh use of 'new'. It is a nice syntax but since our code will contains lots and lots of constructors, there will be just too many of these.... THat is why let's rewrite our constructor function as a regular function:
function makeNode (tag) { var new_node = new Object(); new_node.tag = tag; return new_node; }That's it. Note that the function makeNode is much like our old friend function make. In fact, what if we just get rid of name makeNode and simpy redefine make! Since all our html function use make, we'l then get functions h1, h2, etc creating objects. We need to add one xtra arg - to pass the body:
function make (tag, body) { var new_node = new Object(); new_node.tag = tag; new_node.body = body; return new_node; }So far it's nice but not very usable since we need a print method. Something liek this:
function nodeToString (node) { return "< + node.tag + ">" + node.body.toString() + "</" + node.tag + ">"; }And this function must become a method toString() of each node. Here is how:
function make (tag, body) { var new_node = new Object(); this.tag = tag; this.body = body; this.toString = nodeToString; }Below is a complete example that demonstrates these ideas
...
Here is the complete example