JavaScript (and jQuery) are important in CS304 primarily because of its importance in Ajax, which is a different way for the front-end (a web browser) to interact with the back-end (web server, including middleware and databases). However, we will defer Ajax until next time, because we first need to learn JavaScript and jQuery well enough to make effective use of Ajax.
In short, our destination is Ajax. To get there, we need to know some JavaScript (JS) and jQuery (JQ).
Our plan to get to Ajax is to quickly review the JavaScript language,
but only the parts we need. We'll take a brief look at the DOM, or
Document Object Model and event-handling. Then, we will have an even
quicker, more cursory look at jQuery, because jQuery makes the Ajax
request and response much easier than it is in raw
JS. We will
then take a brief detour to learn what a callback is. This will
prepare us to learn Ajax.
Readings. There are several topics in this collection of readings:
This webpage itself is 12 printed pages long, so a total of about 21 pages.
JavaScript is a dynamic language that is widely supported by modern web browsers. It is similar syntactically to Java, but semantically to Lisp and Python.
If you don't know JavaScript, you should consult these references:
In the following sections, I'll review a few essentials which will help those of you who just need some reminding, but this is not complete, and you should definitely avail yourself of some of the resources above if you don't know JavaScript.
You switch from HTML to JavaScript (JS) using the script
tag. If you use the src
attribute, you can load JS code,
including the jQuery library, from another file or URL, but then the
body of the script
element is ignored.
JS uses syntax like Java, so both // comment
and /*
comment */
work as comments. Statements look like Java, ending
in semi-colons. (JS has a mis-feature that allows semi-colons to be
optional by allowing the parser to guess where they should be; don't
use this mis-feature, because an incorrect guess is hard to debug.)
/* omit this dumb code var x = 3; // magic number is 3 var y = 7; // another mystical number */
Major control structures (if
statements
and for
and while
loops) look just like Java:
var i,j; for( i=2, i < max; i++ ) { // loop over possible factors j = 2; while( j < Math.sqrt(i) ) { if( (i % j)*j == i ) { // j divides i, so i is not prime break; } alert(i+" is prime"); console.log("prime: "+i); }
Useful tools for debugging are built-in functions alert
(which halts the program and pops up a window that must be acknowledged)
and console.log
which writes its argument to a hidden
window that you can find if you poke around in your browser a
bit. Google for browser javascript console
for your browser.
JavaScript has a few scalar datatypes:
true
and false
.
Like Java, the string concatenation operator is +
(the
plus sign). Unlike Java, the +
operator can be
either add (if both operands are numbers) or string
concatenation (if either operand is a string), and this isn't known
until run-time, since JavaScript is dynamically typed. This is an ugly
facet of the language, causing innumerable mistakes, so be careful.
JavaScript has a reasonably nice and convenient Date
object. The Date
function returns an object that captures
the current date and time and has methods to return those values:
var d = new Date(); console.log("Today is "+d.getMonth()+"/"+d.getDate()); console.log("now is "+d.getHours()+":"+d.getMinutes());
JavaScript has two beautiful compound datatypes, arrays and objects. Both have a simple and convenient literal syntax:
// an array of some primes var primes = [2, 3, 5, 7, 11, 13 ]; console.log("we currently have "+primes.length+ " primes."); primes[6] = 17; // add another primes.push(19); // and another
As you would expect, arrays are zero-based and indexed by integers. They support a number of useful array methods as well.
In JavaScript, an object
is a data structure of name-value
pairs, what other languages call hashtables (Lisp, Java), associative
arrays (PHP), dictionaries (Smalltalk, Python), hashes (Perl), and so
forth. (In a later lecture, we'll learn why they are called objects.)
Here's an example:
// an object representing a movie var movie1 = { title: "Dr. Zhivago", director: "David Lean", starring: ["Omar Sharif", "Julie Christie", "Geraldine Chaplin"], release: 1965 } movie1.running_time = 197; // minutes console.log("The title is "+movie1.title);
Note that values can be scalars or compound; here we have an array literal of strings for the stars.
This object literal syntax is so compact and useful that it is now one of the more common representations of data on the web, called JSON or JavaScript Object Notation. JSON objects are commonly sent to and received from servers as the data of an Ajax call.
You can convert a JavaScript data structure of strings, numbers,
booleans, arrays and objects into a string (suitable for sending or
receiving over the web) using the method JSON.stringify
.
You can convert such a string back into the data structure using the
method JSON.parse
.
var movie_string = JSON.stringify(movie1); var movie_copy = JSON.parse(movie_string); if( movie1.title != movie_copy.title ) { alert("This shouldn't happen."); }
An important note on terminology: this concept of turning a data structure into a string, suitable for writing to a file or transmitting across a network, and then reversing the operation to re-create the data structure is a common one in computer science, not reserved just for JavaScript. One standard term for it is Serialization.
In JS, variables don't have types; data does. So a variable can store any type of data, and you don't have to declare the datatype. The following is fine:
var x = "five"; // a string x = 5; // a number x = true; // a boolean x = [2,3]; // an array x = {x: 2, y: 3}; // an object
When creating a variable, you can declare it
using var
. For global variables, the var
is
optional, but you should do it anyhow.
Functions in JS have a very nice literal syntax. The following creates
a function named add5
and invokes it on 2
:
function add5(x) { return x+5; } console.log("the result is "+add5(2));
You can also have an anonymous function. The following
creates an anonymous function that adds four to its argument. The
anonymous function is stored in a variable called add4
. The
last line invokes the function on 3
:
var add4 = function (x) { return x+4; }; console.log("the result is "+add4(3));
Functions and variables share the same namespace, so the preceding two ways to create a function are (nearly) the same. (The only difference is in minor ways such as error messages, where an error in a named function can say what the name of the function was.)
In the example above, is add5
a function or a variable?
It's both: add5
is variable whose value is a function
named add5
. What about add4
?
Is add4
a function or a variable? It's both, because it's
a variable that contains an anonymous function, but since the variable
name can be used anywhere that a function name is used, including in
invoking the function, add4
is a function.
Anonymous functions are used a lot by jQuery programmers, so it's a good idea to get comfortable with seeing them.
A variable is essentially either global
(declared and used
outside any function) or local
(declared inside a
function). The distinction is the critical use of the var
keyword.
var X = 5; // global named capital X function addX(y) { X++; // increments global X return y+X; // refers to global X } function addX2(y) { var X = 3; // new local named capital X X++; // increments local X return y+X; // refers to local X }
(There are other scopes, such as closures, but we won't make much use
of them for now. Also, modern JavaScript now has the let
keyword for local variables. It is similar to var
. The
differences are subtle and rarely important. I'll use var
,
but using let
instead will almost always work.)
One of the key things that you can do with JavaScript is to modify the contents of the browser: changing the structure of the document, including adding and removing content. You can also alter the style of the elements, dynamically changing the CSS classes of elements or directly altering the CSS rules.
The API or Application Programming Interface by which JavaScript can modify the document is called the Document Object Model, or DOM for short.
The DOM entails a lot, but one thing to know is that your document is
a tree of nodes. So, for example, this paragraph is a child of
the BODY
element, and the em
tag earlier is
child of this paragraph. In the next section is an aside
with a gray background, containing a paragraph and an unordered
list, UL
.... And so on.
Many of the following are excellent introductions to the DOM, but they will use the native JavaScript API. The raw DOM is actually not that hard to use from JS, but the jQuery library makes it even easier, so rather than learn two ways to modify the DOM, we'll skip over this and modify the DOM via JQ.
The following is a list of useful but optional references, but you don't need to learn the raw JS API to the DOM, so feel free to ignore those parts.
Because jQuery makes manipulating the DOM easier, let's learn jQuery.
jQuery is a JavaScript library of useful methods for manipulating the document, by which I mean things like this:
It does a few other things as well, including Ajax. For example, those Facebook updates, sending the Gmail, and sending the document changes are all done via Ajax.
jQuery has a small footprint, which means it doesn't take a long time to download to your browser and doesn't take up too much memory once it is loaded. It's well-supported and extremely popular. Google, Facebook, and many other tech companies use and support it.
For extreme brevity, everything in the jQuery library is accessed by
via one function whose name is $
— yes, the dollar
sign character. A synonym of the $
variable/function
is jQuery
, but that's rarely used. After all, it may be
clearer, but it's six times as much typing!
There's a pattern to most jQuery usage:
$(selector).method(arg);
The selector
argument uses CSS syntax to select some set
of nodes in your document to operate on. The method
then
operates on that set in some way. If the method needs to know
additional info, that's supplied in the arguments.
Here are some examples:
// change the CSS of all paragraphs (P elements) to make the text blue: $("P").css("color","blue"); // change the CSS of all elements of class "important" to have red text: $(".important").css("color","red"); // change the CSS of the navbar to be red on white. Notice the use of an // object literal to package up two changes $(".important").css({"color":"red","background-color":"white"}); // add the class "important" to all H2 elements: $("h2").addClass("important"); // change the text of all H1 elements to "Cool Stuff" $("h1").text("Cool Stuff"); // change the HTML of the copyright notice to this year var d = new Date(); $("#copyright").html("© "+d.getFullYear()); // add bananas to the grocery list. $("ul#groceries").append("<li>bananas"); // delete all H3 elements from the document $("h3").remove(); // hide (make invisible via CSS display: none) all paragraphs in the sidebar $("#sidebar p").hide();
We could go on, but you get the idea.
The jQuery API is well documented. Here are some of the methods we used above:
You can learn a lot just by poking around in there and reading some of their examples and notes.
The implementation of jQuery uses a clever trick that can create a great deal of efficiency and brevity. Supplying a selector to the jQuery function and invoking it returns a object that represents the set of matched elements. That object supports the methods like the ones we looked above. Furthermore, most of those methods return the same object as their return value, which means that we can keep operating on the same set, just by invoking another method, chaining them together.
That's all very abstract, so let's see some examples.
This first example is how a novice might do a series of things with some selected objects:
$(sel).addClass('important'); // make them important $(sel).css('color','red'); // make them red $(sel).append("<em>really!!</em"); // add an exclamation $(sel).hide(); // and hide them??
That works fine, but the trouble is that jQuery has to keep finding all the objects, so it wastes a lot of work.
A more experienced or efficiency-conscious person might do the following:
var elts = $(sel); // get a set of DOM objects elts.addClass('important'); // make them important elts.css('color','red'); // make them red elts.append("<em>really!!</em"); // add an exclamation elts.hide(); // and hide them??
That's efficient, but a bit tedious to type. An experienced and terse jQuery coder might do the following:
$(sel).addClass('important').css('color','red').append("<em>really!!</em").hide();
Of course, that's really ugly and hard to read. The important point is that each method is just called on the return value of the one to its left. The layout of the code isn't important, so we are free to lay out the code nicely, maybe with comments. So, the true jQuery expert writes the following (notice the lack of semi-colons, which would interrupt the chain by ending the statement):
$(sel) // get a set of DOM objects .addClass('important') // make them important .css('color','red') // make them red .append("<em>really!!</em") // add an exclamation .hide(); // and hide them??
Note: One drawback of this technique of returning
a set of matched elements
is that the empty set is a perfect
valid set, so jQuery is perfectly happy to do all those operations above
on an empty set of elements, thereby doing nothing, and never give you a
peep of complaint or warning. So if your jQuery isn't working and
there's no error message, scrutinize your selector expressions. In
fact, I often end up doing something like this when I'm debugging:
var x = $(sel); console.log("matched "+x.length+" elements");
If that shows that the number of matched elements is zero, operating on the set will be pointless.
In fact, this debugging technique is useful enough that I wrote a jQuery plug-in to do it for me very easily. See bounds plugin if you're interested.
Before we get to events and Ajax, we should take few minutes to look at operating on the structure of the document. We'll use jQuery to do this. We'll start with adding a list of prime numbers to the web page. First, we need to have a destination for them:
<div id="prime-container"> <p>Our primes: <ul id="prime-list"> </ul> </div>
Notice that the list is empty. This seems odd, but it happens all the time with dynamic documents. The static document is empty, just providing structure for the content, and all the interesting stuff is added dynamically.
Now the code to add some primes to that list:
function addPrimes( primes ) { var i; for( i=0; i < primes.length; i++ ) { $('
Here it is:
Our primes:
You might wonder what the '<li>' does as the argument of the
jQuery function, since it's not a CSS selector. What happens is that
jQuery creates the given element, but it is not (yet) attached
to the document. Here, we attach it to the document
with the appendTo
method.
An alternative way to do this is to build the entire list up and only attach it to the document at the end. This is more efficient, since the document only has to be re-rendered once for the list, as opposed to once for each element.
<div id="prime-container2"> <p>Our primes: </div>
Here's the variant JS/JQ code:
function addPrimes2( primes ) { var i; var listElt = $('
Here it is:
Another list of our primes:
In addition to letting us work with the DOM, jQuery lets us work with events. Events are important ways of hooking into user behavior: a user clicking on something or mousing over something is an event, and we can make things happen when that event occurs.
The way that event-handling in the DOM works is that you can say:
when event E occurs to DOM element D, please invoke function F.
Here's a partial list of some common DOM events:
We'll mostly be working with .click()
.
Let's make that more concrete. At the end of this section, I'll put a
DIV whose ID is fred
. Let's write some code that would turn
that element a random color when we click on it. First, a useful but
uninteresting function:
Now, the event-handling magic. This says to change the DOM element
whose ID is fred
to a random color whenever the code is
executed.
// jQuery magic to turn it a random color: $("#fred").css('color',randColor());
Okay, very nice, but that's not yet what we want. We'd like the user
to be able to turn the header a random color just by clicking on it.
So, one step on the way to do that is to package up that code into a
function, say turnFredRandomColor
:
function turnFredRandomColor() { // jQuery magic to turn it a random color: $("#fred").css('color',randColor()); }
Then, whenever we want to turn that element a random color, we just invoke the function:
turnFredRandomColor();
However, we want the user to be able to have that function invoked by
clicking on the element. More precisely, we want to say that whenever
the #fred
element gets a click
event, we'd
like the turnFredRandomColor
function invoked. jQuery
provides a very easy way do to this, using the same pattern we've seen
many times:
$("#fred").click(turnFredRandomColor);
Try it!
Now, there are some very important points to make: we
are not invoking the turnFredRandomColor
function
right now. Instead, we are giving it to the click
method,
much like we gave the 'color'
string and the value of
the randColor
variable to the css
method
above. That is, the function is merely a piece of data that is being
passed as an argument. It is not being invoked now.
To invoke a function we give its name (or, equivalently, a variable
whose value is the function) followed by parentheses containing
any arguments to be passed. Since we are not
invoking turnFredRandomColor
now, it's not followed by
parentheses.
When does it get invoked? The browser will invoke it when the event happens. The function is an event handler.
The function is also an example of a callback. A callback is a general Computer Science term for a function that is invoked later, when something happens or has happened. They're used in graphics programming, processing data, GUI programming and lots of other situations.
By the way, an experienced jQuery programmer wouldn't bother to devise
that cumbersome name (turnFredRandomColor
) for a function
that they are never going to refer to again after handing it to
the click
method. Instead, they use an anonymous
function literal, putting all the important code right where they need
it:
$("#fred").click(function () { var colors = ['red','orange','yellow','green','blue','purple']; var randIndex = Math.floor(Math.random()*colors.length); var randColor = colors[randIndex]; // jQuery magic to turn it a random color: $("#fred").css('color',randColor); });
The whole function literal is the argument of the click
method — notice the close paren on the last line, after the
closing brace of the function literal.
One of the events that will be important in using Ajax is the event of some data being loaded from a server. We're getting closer to our destination now.
this
VariableThe function turnFredRandomColor
lacked some
flexibility. It always operated on the #fred
element (that
is, the DOM element whose ID is fred
; I'll use the CSS
language to abbreviate my descriptions and reinforce the language).
A more flexible function would instead operate on this
,
which is a special dynamic variable in the JS language. On every method
call, the variable this
is bound to the object that the
method is invoked on, much like this
variable in Java. We
haven't learned OOP syntax in JavaScript, and we probably won't, but it
doesn't take us much out of our way to mention this
.
In addition, the this
variable is bound to the DOM element
that had the event happen to it. In other words:
when event E occurs to DOM element D, please invoke function F and bind.this
to D
How is that useful? Try clicking on the different list items in the following list:
Here, I've changed the function to set both the text color and the background color. Most combinations will be unreadable, but you can always click again. The point is that each acts independently.
Here's the code:
Things to note:
"#groceries li"
selects every LI that is a descendant of #groceries
, so it selects 7 DOM elements here.this
which will be the actual LI that you clicked on
this
in the jQuery function, so that it can use jQuery methods on the DOM element
A complex result from a very compact notation!
But wait, there's more. The grocery list example above is good, but as
we mentioned, it creates seven identical event handlers, which isn't
efficient. jQuery allows us to use event bubbling to attach the event
handler to the single ancestor, and operate on the event target
(which is bound to this
). So modern best practice is to use
a delegated event handler, delgative the work to a single,
ancestor. Here's an example using a list of my favorite movies:
Here's the code:
We'll learn more about event bubbling and delegated handlers next time; this is a pre-view. [an error occurred while processing this directive]