cs304 logo

JavaScript and jQuery

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).

Plan

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

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.

The SCRIPT tag

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.

Basic Syntax

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.

Datatypes

JavaScript has a few scalar datatypes:

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.

Variables and Scope

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

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.)

DOM: the Document Object Model

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.

DOM References

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

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!

jQuery Usage

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("&copy; "+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.

jQuery API

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.

Method Chaining

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.

Building Structure

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++ ) {
        $('
  • ').text( primes[i] ) .appendTo('#prime-list'); } } addPrimes( [2, 3, 5, 7, 11, 13, 17] );
  • 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:

    DOM Events

    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.

    The this Variable

    The 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:

    A complex result from a very compact notation!

    Delegated Event Handlers

    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:

    1. Dr. Zhivago
    2. Lord of the Rings
    3. Schindler's List
    4. Black Panther

    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]