JavaScript, jQuery, and DOM model interaction

Lead Image © radiantskies, 123RF.com

Lead Image © radiantskies, 123RF.com

Consider the Object

The JavaScript language has developed into an important programming language. We explain the basics and provide examples for accessing an open HTML page through the DOM interface.

With most web applications and dynamic websites, the program logic is on the server. A program written in PHP, Java, Python, or Perl generates HTML pages tailored to user requests. However, the browser itself includes a complete and very powerful run-time environment with its JavaScript interpreter.

JavaScript programs respond to user input and access the opened HTML page via a so-called DOM API. This interface between JavaScript and HTML lets you change attributes for each tag on the page, such as color, size, or visibility. It also lets you remove sections of a page or insert HTML elements anywhere (Figure 1).

Figure 1: The JavaScript run-time environment, which communicates via the DOM interface with the HTML page in both directions, responds to user input, such as mouse clicks, and modifies content and HTML tag properties.

Old Friends

Apart from being object oriented, JavaScript is very similar to the C programming language and shares many of the basic structures (loops, conditions, operators) of other languages, from Java to Perl. That's what makes the switchover so easy, as long as you use only the given procedural elements of the language that arrange the source code into routines to satisfy specific tasks.

You can avoid object orientation for smaller applications. However, JavaScript has some idiosyncrasies when it comes to larger programs in which object orientation is indispensable. The next part of this article is dedicated to its special variant of encapsulating data and inheritance.

The choice of the name JavaScript has more marketing than technical origins. Apart from a base inventory, which Java shares with C and other languages, JavaScript adopts only the dot notation object.attribute from its big brother. Unlike Java, JavaScript is not a strictly object-oriented language.

Also, JavaScript integrates elements of functional programming that Java lacks to this day. Functional programming languages can define nameless variable-bound functions (anonymous, or lambda, functions) that can also be used as function parameters. You can find a complete overview of the JavaScript language apart from its object-oriented aspect on the web [1].

Help Requested

Hardly any programmer still speaks directly to the DOM API. Its implementation differs too much from browser to browser, although the number of differences have been steadily decreasing in recent years. Additionally, it is often seen as cumbersome and bulky. Thus, most programmers use the jQuery [2] library as an interim layer. This introductory article focuses exclusively on that approach.

The first sample program uses a loop to add four colored boxes to an empty page. As an example of interactivity, clicking one box opens a dialog with text depending on its color. The lines in Listing 1 already show many of the foundations of JavaScript programming, and Figure 2 shows the result.

Listing 1

Four Interactive Boxes

01 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
02 <script type="text/javascript">
03 $(function(){ //execute when page is completed
04   var colors = ["red", "green", "blue", "yellow"];
05   var names = ["Red", "Green", "Blue", "Yellow"];
06   var sayings = {
07     "Red":"Roses are red...",
08     "Green":"A hedge between keeps friendship green.",
09     "Blue":"... violets are blue.",
10     "Yellow":"The face of cowardice is yellow."
11   };
12
13   for (var i in colors){
14     var div = $("<div>");
15     div.css({"height":"50px", "width":"150px", "background-color":colors[i]});
16     div.addClass("colorbox");
17     div.html(names[i]);
18     $("body").append(div);
19   }
20
21   $(".colorbox").on("click", function(){
22     var clicked = $(this);
23     var color = clicked.text();
24     alert(sayings[color]);
25   });
26 });
27 </script>
Figure 2: The browser writes with JavaScript what looks like simple, static content onto a blank page. Clicking a color brings up a matching text pop up.

Line 1 pulls in the jQuery library used for manipulating HTML code. If the <script> tag includes a src attribute, it references an external JavaScript file. In this case, it points to the URL of Google's version of jQuery, which saves having to download the library. The second <script> tag contains no attributes but embeds the entire program code. The type="text/javascript (line 2) references the embedded script's language.

Dollar Signs

The dollar sign jumps right out at the start and identifies a jQuery function. The first line of the script (line 3) is often puzzling for newcomers. It opens the $() statement that ends just before the ending </script> tag.

The browser immediately executes JavaScript code as soon as it parses the HTML page. However, it could happen that the browser hasn't finished rendering the page yet, in which case the script will not find certain page elements and will fail.

To avoid this problem, the function $() encapsulates the part of code that jQuery should execute only after the browser finishes loading all the HTML code. In the example, $() encloses the entire JavaScript code. To be exact, it doesn't enclose the code itself but receives as a parameter a nameless (anonymous, or lambda) function that contains the actual program code.

Apart from the fact that the code enclosed by the anonymous function is guaranteed to execute only after the browser has rendered the page, the functionality does not change.

Anonymous, Yet Present

Anonymous functions are indispensable ingredients of JavaScript. Event handlers that respond to user input (e.g., line 22 in Listing 1) wouldn't work without them. A "function without a name" is quite literally that. Immediately following the function keyword are parameters enclosed in parentheses (in this case they're empty, because you do not need to pass parameters to the function). Lambda functions behave just like simple values (numbers or strings): You can assign them to variables or pass them as parameters to functions.

The first two lines of the lambda function define arrays of various strings with the syntax <array> <index> that define content. Array keys can be zero or positive integers only. Containers with strings as keys, also called associative arrays or hashes in Perl, are called "objects" in JavaScript. Defining objects requires JavaScript Object Notation (JSON), which many other languages now use.

In JSON, curly braces enclose comma-separated lists of key-value pairs (lines 6-11, Listing 1). You access them with the dot notation inherited from Java (<object>.<attribute-name> ). Whenever the attribute name is contained within a variable, the array syntax is as follows:

var name = "attribute";
var value = object[name];

Incidentally, JavaScript variables are always declared with the var keyword – only then can the interpreter determine its scope. In this way, variables declared inside the function remain invisible on the outside.

The sample code stores the color codes of the boxes in the two arrays. The object defined on the next line contains the sayings, one of which the browser displays when you click the corresponding color box.

Out of Nowhere

The first step is to render the boxes on the empty page, which occurs in the loop starting in line 13. The for loop iterates over the existing forms to determine the elements of a container. Loop variables in JavaScript, however, contain the string value of a container; the integer values are read from <container> <loop-variable> (lines 15 and 17).

The first line of the loop statement includes the jQuery method with the dollar sign in its simplest form. If you pass $() to HTML code, it creates a valid, but not yet rendered, HTML fragment. The browser automatically adds the missing </div> end tag. However, jQuery encapsulates the HTML fragment in a jQuery object before returning it, which you can use to call all methods.

Among these methods is css() on line 15, which assigns a style attribute to the div just created and expects a JavaScript object with attribute-value pairs. Thus, the following attribute in the example is created:

style="height:50px; width:150px; background-color: red"

The next line embeds the color text string in the <div> element and the one after that adds a class to the element:

class="colorbox"

The $(<selector>).append(<element>) line is probably the most commonly used method in jQuery. The selectors from the first part of the statement, especially, make life a lot easier for developers. Here you find largely the same terms used by Cascading Style Sheets (CSS) [3] to bind style definitions with HTML elements. A tag name extracts all elements with the tag of a certain type, a <.class-name> extracts all elements of class="<class-name>" , and #<id> extracts the element with id="<id>" .

You can attach as many filter methods (e.g., first() , last() , and parent() ) as you want to the $(selector) . The comprehensive jQuery documentation [4] [5] describes the entire gamut. With these selectors, you can pick any element from a complex HTML page by using a single line of code that works reliably on all major browsers – that's what makes jQuery so popular.

The sample code needs only one simple selector, $("body") , that works with the single <body> tag of the page. The append(div) method adds the <div> tag to the end of the page body that was given a style, text, and colorbox class attribute in the meantime. Thus, four loop iterations over the colors array create the four different color boxes.

Eventfulness

The only thing missing is the event handler that creates a dialog with the appropriate saying from the sayings object based on the color box that the user clicks. The $(".colorbox") statement selects all the tags of the colorbox class, that is, all the inserted <div> s.

To set the event handler, jQuery provides the on() method that irons out the browser characteristics – as did the selectors. It expects the name of the event as its first parameter (e.g., click ) and a lambda function as its second parameter, which occurs with the event. The lambda function provides jQuery an object that contains a lot of information about the current event [6].

The this keyword plays a major role in this example. The JavaScript event handlers run within the context of each DOM element that triggers the event, and this is the pointer to the current object. Here it points to the clicked color box.

To process the element in this , you call up the $() base function with this as its parameter and assign the result to the clicked variable. You can now use clicked.<method()> to apply any jQuery function to the clicked color box. In this case, it uses text() , which you already encountered when setting the text inside the box.

Without a parameter, this action returns the text contained in the tag, which is the initial-uppercase color name in this example. Under this key, the sayings object has a saying applied to each color. Using sayings color , you can pass it on to the browser's alert() method, which opens the dialog corresponding to the color-based saying.

Higher Orders

Private and public attributes, objects of a class in any number of instances, and inheritance all can be implemented in JavaScript, albeit with a completely different functionality than classic object-oriented languages. Such languages generate classes with the class keyword, and new handles object instances that all get the same methods and data attributes from the class definition. JavaScript also recognizes the new keyword, but with class , the function keyword plays an entirely different role.

The last two lines of Listing 2 have the sayHello method to handle two different instances of greeter and encapsulate different values for the greeting that are not accessible from the outside – classic object orientation out of the box.

Listing 2

Playing with Objects

01 var Greeter = function(who){
02   var toGreet = who;
03
04    this.sayHello = function(){
05     alert("Hello, "+ toGreet + "!");
06   }
07 };
08
09 var greeter1 = new Greeter("World");
10 var greeter2 = new Greeter("John");
11 greeter1.sayHello(); //-> Hello World!
12 greeter2.sayHello(); //-> Hello John!

Incidentally, to execute the code example, there's no need to write it in a file that you open in the browser. All modern browsers provide a console that runs directly entered code as a test. With Firefox, as of version 4, you can do so using Ctrl+Shift+K.

The Firebug [7] add-on, which includes a helpful debugger, is still better than the built-in Firefox console for larger projects (Figure 3). It stops the program at marked places and shows the value of all variables at the breakpoint. Using console.log(<value>) , you provide a value for the error search at the console. For objects, the add-on shows the attributes in JSON notation.

Figure 3: Even though Firefox from version 4 provides a JavaScript console (top) to test short code blocks, the Firebug add-on can do much more, such as provide a debugger that pauses the code on demand.

Functionality

Those who don't know JavaScript well enough might ask how a function takes on the role of a class definition. The answer is that functions in JavaScript are like objects. Just as with JSON-defined objects, you can give them new attributes via <object-name>.<attribute-name> . The same notation applies to reads.

These attributes can take simple values as well as functions (e.g., sayHello() ) because JavaScript basically handles both types the same. Attributes with the "function" type behave just like methods in other object-oriented languages:

<Object>.<Name>(<Parameter>)

The this keyword in the sample code behaves as in other languages: It points to the object it is in. The this.sayHello method therefore sets the value for the sayHello attribute of the function. The sayHello() attribute then behaves with the object instances formed by new Greeter like a public method in C++ or Java objects.

Inclusiveness

Perhaps more puzzling is the line var toGreet = who; . Caveat: In JavaScript, you can define inner functions that access variables from an outer function. Of course, the outer function can't access variables in the inner one, because that would result in a single scope of universal variables, which could cause a conflict.

Both calls of greeter1.sayHello() and greeter2.sayHello() also show that the who parameter value is stored in toGreet , even though the Greeter function was already executed. The reason is that the variable-bound functions in JavaScript have their run-time environment wrapped up in them. In concrete terms, this means all variables in the scope of sayHello that the function accesses will stay in memory, as long as the function itself is bound to a variable. The greeter1 and greeter2 functions are the ones responsible for this, taking sayHello() as an attribute. This technique is known as "closure."

The closure principle is really quite simple. The JavaScript interpreter goes through the code once before executing it and notes which parts of the program access which variable. In regard to how it decides how long a variable should stay in memory, there's a simple answer: Everything still needed remains in memory.

Parent and Child

I still haven't provided an explanation for the new keyword that creates the two object instances greeter1 and greeter2 (which produce the different values for toGreet ) out of the Greeter function definition.

Here, the function() and the object are identical, so the function changes the attribute assignment this.<attribute-name> itself. After executing the function, the new call sets the function-identical object to the variable greeter1 or greeter2 , just as the function will end with return this .

Object instances from a common definition are created in JavaScript via the call new <function-variable>(<parameter>) . Inside the function stored in <function-variable> are variables like private data fields or – if its content is a function – a private method. On the other hand, the this.<attribute-name> creates public attributes or methods.

Hard Legacy

Imagine having a class with five methods defined and wanting another new one to take over four of them and change the fifth one. This is a clear case for inheritance. Listing 3 shows how that happens in JavaScript. In the code, the class derived from Greeter() subsequently receives the Bavarian greeting "Pfiat' di."

Listing 3

Inheritance

01 var Greeter = function(who){
02   this.toGreet = who;
03    this.sayHello = function(){
04     alert("Hello, " + this.toGreet + "!");
05   }
06 };
07
08 var extendedGreeter = function() {
09   this.bavarianHello = function(){
10     alert("Pfiat' Di," + this.toGreet + "!");
11   }
12 };
13 extendedGreeter.prototype = new Greeter("Vroni");
14 greeter3 = new extendedGreeter('Vroni');
15 greeter3.sayHello(); //-> Hello, Vroni!
16 greeter3.bavarianHello(); //-> Pfiat' Di, Vroni!

The code defines two functions, Greeter and extendedGreeter . In Greeter , I've transformed the earlier toGreet into a public attribute. The extendedGreeter() defines a method, bavarianHello() , which outputs the Bavarian greeting. This step is done by drawing on the toGreet attribute defined in Greeter , which works because the first statement after the Greeter and extendedGreeter function definitions combines them. This line binds the instance of Greeter to the prototype attribute of the class definition for extendedGreeter .

All JavaScript objects endow a prototype attribute that does not have an explicit definition with a fixed task: All instances created with new extendedGreeter() inherit the attributes of the objects bound to prototype .

However, this inheritance applies only to public attributes defined by this.<attribute-name> . Thus, you can change the Greeter class to expand it. Figure 4 shows an object dump of the Firebug console with the inherited method sayHello() and the newly added bavarianHello() .

Figure 4: A dump of the greeter3 object in the Firebug console (the second green line on the left) shows that the inheritance in this example works. The object contains the attribute toGreet along with the methods bavarianHello() and sayHello().

Collateral Damage

Inheriting a private attribute works in JavaScript only indirectly – with different effects on performance [8] [9]. The fact that no combination of the two most powerful features of JavaScript is possible  – creation of object instances with new and closure along with inheritance based on the prototype attribute – has brought the language much criticism.

The object orientation in JavaScript shows its immaturity. Like many web standards, it's a result of the browser war between Netscape and Microsoft. In its pursuit to conquer its competitor, Netscape raised the language to a standard a bit too hastily.