Chapter 11. Effective JavaScript

It matters not what someone is born, but what they grow to be.

J. K. Rowling

Developed for the web in the 1990s, JavaScript is widely used today outside the realm of web applications. For example, you find JavaScript used to write application extensions for Adobe Photoshop, Google Chrome, and Mozilla Firefox. It’s also used to create native mobile applications; for example, PhoneGap or Appcelerator Titanium. JavaScript is also used for some flavors of server-side applications, most noticeably Node.js.

JavaScript’s fate is curious. Born to be the language of the first web fan-boys, it has been through a series of highs and lows since the early 1990s. At some point, its use was boosted by the advent of Dynamic HTML and calmed down in only a few years with the consolidation of Java Server Pages and ASP.NET, which brought the focus of web development back to the server side. JavaScript revived and sprang back to life with Ajax and was fortified by Node.js and mobile frameworks. As the quote at the beginning of the chapter reminds us, it doesn’t matter what JavaScript was born; it counts a lot more what it has grown to be.

JavaScript is a programming language that is easy to get acquainted with; at the same time, though, it is not trivial to master.

All web applications these days are required to add more and more client-side features. I’m not simply talking about client-side validation and facilities, I’m talking about full Ajax applications. A full Ajax application is a web application that reduces page swaps to a bare minimum. A full Ajax application is centered on very few core pages whose user interface changes interactively following the user’s action. Obviously, you do a lot of work on the HTML Document Object Model (DOM), and you do that work by using JavaScript.

Note

As an ASP.NET MVC developer, you should be ready to write more and more JavaScript code. More important, you should be ready to import more and more JavaScript code written by others. These two are not mutually exclusive options. As a matter of fact, there are nearly no websites today that are written without importing features from jQuery and a few other libraries, such as AngularJS, KnockoutJS, or Modernizr.

Revisiting the JavaScript language

JavaScript is an unusual language because it was created for non-developers; it has remarkably low barriers to entry, yet it’s flexible enough that experts can use it to do nearly everything they need to, as well. As I see things, the challenge today is for average JavaScript developers to create effective content with some good design while keeping readability and maintainability at a decent level. For this reason, this section aims at refreshing some basic JavaScript facts.

Language basics

JavaScript code is interpreted, meaning that JavaScript programs need an environment in which to run. Their natural habitat is the web browser. The language syntax is driven by one solid standard: ECMA-262, which is also catalogued as ISO/IEC 16262:201. Over the years, a few JavaScript dialects have arisen based on different implementations of the standard. Sometimes, these dialects have an entirely different name such as JScript, JScript.NET, ActionScript, or EcmaScript. In the end, though, it’s the result of different engines that process the JavaScript source code. Popular engines usually associated with web browsers are V8 (Google Chrome), Chakra (Microsoft Internet Explorer), Gecko (Firefox), and Opera.

Let’s briefly navigate through the basics of the language and refresh concepts that, frankly, most ASP.NET developers just picked up but never studied thoroughly.

The type system

The JavaScript type system comprises primitive types and a few built-in objects. When you write JavaScript code, however, the range of types that you can work with is actually larger. In addition to using built-in objects, you can also rely on objects provided by the host. The canonical example is the window object that the most common JavaScript host—the web browser—publishes into the JavaScript engine.

Primitive types are number, string, Boolean, null, undefined, Object, and function. The built-in objects are Array, Math, Date, Error, RegExp, plus wrapper objects for a few primitive types: string, Boolean, and number.

The number type represents floating-point numbers with 0 or more decimal places. There are no separate types for integers, long, or bytes. The range of numbers is between –10308 and 10308. You can write numbers in decimal, octal, or hexadecimal format. The special number NaN represents the result of a math operation that makes no sense (such as division by zero).

The string type represents a row of 0 or more characters; it doesn’t represent an array. Individual characters are represented as strings of 1 character. Special characters in a string begin with a back slash (), such as to indicate a carriage return. The content of a string is bracketed in matching pairs of single or double quotes. The primitive value is wrapped in a string object that adds a few methods, including split and substring.

Null vs. undefined

A JavaScript variable that doesn’t contain a meaningful value can be assigned a null value as well as undefined. What’s the difference? When it comes to nullness, JavaScript introduces a subtle difference that many higher-level languages such as C# and Java miss.

In particular, undefined is the default value that the run-time environment assigns to any variables being declared. Most remarkably, an unassigned variable contains undefined, not null or some default value as in C#. On the other hand, null is a value that still represents a null, empty, or non-existing reference, but it has been explicitly assigned by the developer. In a nutshell, a variable set to undefined has never been touched by any code; a variable that holds null was assigned that value via some path in your code.

If you run typeof on an unassigned variable, you get undefined—it’s a distinct type by itself. If you run typeof on a variable assigned with null, you get object. Pay attention to the following code:

var x;             // hence, undefined
var y = null;

What happens if you compare x and y? If you use the == operator, you get true, meaning that undefined ultimately evaluates to null. If you compare using the === operator, you get false: the two variables hold the same value but are of different types.

Local and global variables

In JavaScript, a variable is a storage location that is not restricted to always storing values of a fixed type. When assigned a value, variables take on the type of the data being stored. For this reason, a JavaScript variable might change its type quite a few times during its lifespan.

var data = "dino";   // now data is of type string
data = 123;          // now data is of type number

This behavior is different from the typical behavior of C# variables, unless C# variables are defined of type dynamic in .NET 4. Variables spring into existence the first time they’re used; until then, they hold a value of undefined.

When defining variables, you should always use the var keyword as a hint to the parser and yourself. The var keyword is not strictly required, but it’s highly recommended. Variables defined within a function are scoped to the function if declared by using var. If not, variables are treated as global, but they remain as undefined until the function executes once. Variables declared in the global scope are always global regardless of whether var is used.

<script type="text/javascript">
  var rootServer = "http://www.expoware.org/";   // global
  section = "mobile";                            // global
</script>

<script>
  function doSomething() {
     var temp = 1;                               // local
     mode = 0;                                   // global, but undefined until called
  }
</script>

The JavaScript run-time environment stores global variables as properties of hidden objects referenced through the this keyword. Browsers often mirror this object via the window object.

In any programming language, coding is (much) easier if you can use global variables. However, globals have downsides, too. A critical downside is the risk of name collisions between variables defined in different parts of your code, third-party libraries, advertising partners, and analytics libraries. A name collision combined with the dynamic typing of JavaScript variables might lead you to inadvertently modify the state of the application with unpleasant anomalies at run time.

Consider that creating globals unwillingly is easy, too: miss a var and you end up with a global; mistype a variable name in an assignment and you have a fresh new global. This latter feature is possible because with JavaScript you can use a variable without declaring it first. When you need to use global variables, a good technique is creating them as properties of a wrapper object. You place the following code in a JavaScript file that you link from every page:

var GLOBALS = (function() { return this; }());

Next, you use GLOBALS.Xxx, where Xxx is any global variable you might need to have. This at least ensures that your global variables will stand out.

An even better approach is to simply not use the global namespace and the this object and instead use a brand new global dictionary object. You initialize it as below:

var GLOBALS = GLOBALS || {};

If you have multiple JavaScript files in your application that need to reference GLOBALS, you place the preceding line at the top of each file. The || in the syntax ensures that the dictionary is not reinitialized every time a file is processed.

Note

JSLint (available at http://www.jslint.com)—an online tool for static analysis of JavaScript code—does help catch antipatterns in your code, including the lack of var keywords.

Variables and hoisting

Hoisting is a JavaScript feature with which developers can declare variables anywhere in the scope and use them anywhere in that scope. In JavaScript, you can first use the variable and then declare it (as var) later, as demonstrated here:

function() {     // Not allowed in C#
    mode = 1;
    ...
    var mode;
}

The overall behavior is as if the var statement was placed at the top. Historically, the feature was introduced to keep as low as possible the entry barrier to JavaScript. When you use JavaScript to write significant portions of code, however, hoisting is a clear source of confusion and becomes error prone. A good habit consists of placing all your variables at the top of each function—preferably, in a single var statement, as shown here:

function() {
   var start = 0,
       total = 10,
       sum = function (x,y) {return x+y;},
       index;
   ...
}

JSLint can be instructed to catch the use of multiple var statements in a single function and remind you about this pattern.

Objects

The primary reason for not cataloging JavaScript as an object-oriented language is that the definition of an object you get from JavaScript is different from the commonly accepted idea of an object you get out of classic object-oriented languages such as C++ or C#.

In JavaScript, an object is a dictionary of name/value pairs. The blueprint of the object is implicit, and you have no way to access it. A JavaScript object usually has only data, but you can add behavior. The (explicit) structure of an object can change at any time, with new methods and properties; the implicit structure never changes. Because of the implicit blueprint, any apparently blank object in JavaScript still has a few properties, such as prototype. (I’ll return to prototype later.)

Keep in mind that variables that store objects don’t actually contain the object’s dictionary; they just reference the object’s bag of properties. The bag of properties is distinct from the variable store, and different variables can reference the same bag of data. The new operator creates a new bag of properties. When you pass an object around, you just pass the reference to the bag.

Adding a member to a JavaScript object works only for that particular instance. If you want to add a new member to all instances being created of that type, you have to add the member to the object’s prototype.

if (typeof Number.prototype.random === "undefined") {
    Number.prototype.random = function() {
        var n = Math.floor(Math.random() * 1000);
        return n;
    };
}

Note

Augmenting the prototype of native objects is considered by some developers a bad practice (or at least, arguable) because it makes the code less predictable and can hurt maintainability. Overall, I believe this is questionable and mostly a matter of perspective. I’d say this: Be aware of potential issues and do as you feel comfortable.

You use the Object type to create aggregates of values and methods, which is the closest you come in JavaScript to C# objects. The direct use of the Object’s constructor (as shown in the following) is usually disregarded:

var dog = new Object();
dog.name = "Jerry Lee Esposito";
dog.getName = function() {
     return this.name;
}

A better approach entails using an object literal, as demonstrated here:

var dog = {
   name: "Jerry Lee Esposito",
   getName: function() {
      return this.name;
   }
};

If you use the Object’s constructor, the interpreter has to resolve the scope of the constructor call. In JavaScript, there’s no guarantee that no local object exists in the scope with the same name as a global object. Therefore, the interpreter must walk up the stack to find the nearest definition of the constructor that applies. In addition to this performance issue, using the constructor directly also doesn’t transmit the sense of objects as dictionaries, which is a key point of JavaScript programming.

Functions

In JavaScript, a function is a bit of code bundled up into a block and, optionally, given a name. If a function is not given a name, it’s an anonymous function. You treat functions like objects: They can have properties, and you can pass them around and interact with them.

In JavaScript, anonymous functions are the pillar of functional programming. An anonymous function is a direct offshoot of lambda calculus or, if you prefer, a language adaptation of old-fashioned function pointers. Here’s an example of an anonymous function:

function(x, y) {
   return x + y;
}

The only difference between a regular function and an anonymous function is in the name (or lack thereof).

You use functions for two main reasons: for creating custom objects and, of course, for defining repeatable behavior. Of the two reasons, the former is the most compelling in JavaScript. Consider the following code:

// The this object is implicitly returned
var Dog = function(name) {
  this.name = name;
  this.bark = function() {
     return "bau";
  };
};

To use the Dog object, you need to instantiate it by using the classic new constructor, as illustrated here:

var jerry = new Dog("jerry");    // OK
var hassie = Dog("hassie");      // Doesn't throw but, worse, may alter the application's state

The tricky thing is that if you forget the new operator, you won’t get any exception and your code just runs, but the this object being used is now the global this object. This means that you’re potentially altering the state of the application. Here’s a safe countermeasure:

var Dog = function(name) {
   var that = {};
   that.name = name;
   that.bark = function() {
      return "bau";
   };
   return that;
};

The difference is that you now explicitly create and return a new object named that. This is familiarly known as the “Use-That-Not-This” pattern.

Object-orientation in JavaScript

There was a time when JavaScript code in webpages was limited to a few lines of basic manipulation of the DOM. There was no need to design this code into reusable blocks and attach it unobtrusively to page elements. Although the average level of JavaScript code complexity is nearly the same as in the recent past, the quantity of it you need to have on each page is rising dramatically. Forms of packaging are required because today, JavaScript code is like a small application on its own. You need to make gains in reusability and also (if not especially) in maintainability. Furthermore, you need to ensure that your code runs in isolation because in JavaScript, it’s too easy to miss variables, spoil globals, and mistype names without a clear indication that you have done so. In this regard, JSLint is a great help, but it’s not like a compiler.

To top off the discussion about the basics of the JavaScript language, let me introduce closures and prototypes—two approaches you can take to implement object-orientation in JavaScript.

Making objects look like classes

Before I get to closures and prototypes, let me say a few more words on the native Object type and its usage. As mentioned, you can use the new keyword to create a new dictionary-like object. Next, you stuff data into it and add methods by wiring functions to property names. Here’s an example:

var person = new Object();
person.Name = "Dino";
person.LastName = "Esposito";
person.BirthDate = new Date(1979,10,17);
person.getAge = function() {
  var today = new Date();
  var thisDay = today.getDate();
  var thisMonth = today.getMonth();
  var thisYear = today.getFullYear();
  var age = thisYear-this.BirthDate.getFullYear()-1;
  if (thisMonth > this.BirthDate.getMonth())
      age = age +1;
  else
  if (thisMonth == this.BirthDate.getMonth() &&
      thisDay >= this.BirthDate.getDate())
      age = age +1;
  return age;
}

What you have is an object modeled after a person; you don’t really have a Person object. As you saw earlier, this has both readability and performance issues. In addition, the object is sparsely defined over multiple lines.

Closures and prototypes offer alternate ways to define the layout of a type, and they are the native mechanisms to use for doing object-oriented programming in JavaScript.

Using closures

A closure is a general concept of programming languages. Applied to JavaScript, a closure is a function that can have variables and methods defined together within the same context. In this way, the outermost (anonymous or named) function “closes” the expression. Here’s an example of the closure model for a function that represents a Person type:

var Person = function(name, lastname, birthdate) {
   this.Name = name;
   this.LastName = lastname;
   this.BirthDate = birthdate;
   this.getAge = function() {
      var today = new Date();
      var thisDay = today.getDate();
      var thisMonth = today.getMonth();
      var thisYear = today.getFullYear();
      var age = thisYear-this.BirthDate.getFullYear()-1;
      if (thisMonth > this.BirthDate.getMonth())
          age = age +1;
      else
         if (thisMonth == this.BirthDate.getMonth() &&
             thisDay >= this.BirthDate.getDate())
             age = age +1;
      return age;
   }
}

As you can see, the closure is nothing more than the constructor of the pseudo-class. In a closure model, the constructor contains the member declarations, and members are truly encapsulated and private to the class. In addition, members are instance based, which increases the memory used by the class. Here’s how you use the object:

var p = new Person("Dino", "Esposito", new Date( ... );
alert(p.Name + " is " + p.getAge());

The closure model gives full encapsulation but nothing more. To compose objects, you can only resort to aggregation.

Using prototypes

The prototype model entails defining the public structure of the class through the JavaScript prototype object. The following code sample shows how to rewrite the preceding Person class to avoid a closure:

// Pseudo constructor
var Person = function(name, lastname, birthdate) {
    this.initialize(name, lastname, birthdate);
}

// Members
Person.prototype.initialize = function(name, lastname, birthdate)  {
    this.Name = name;
    this.LastName = lastname;
    this.BirthDate = birthdate;
}

Person.prototype.getAge = function() {
    var today = new Date();
    var thisDay = today.getDate();
    var thisMonth = today.getMonth();
    var thisYear = today.getFullYear();
    var age = thisYear-this.BirthDate.getFullYear()-1;
    if (thisMonth > this.BirthDate.getMonth())
        age = age +1;
    else
       if (thisMonth == this.BirthDate.getMonth() &&
           thisDay >= this.BirthDate.getDate())
           age = age +1;
    return age;
}

In the prototype model, the constructor and members are clearly separated, and a constructor is always required. As for private members, well, you just don’t have them. The var keyword that would keep them local in a closure doesn’t apply in the prototype model. So, you can define a getter/setter for what you intend to be properties, but the backing field will remain accessible from the outside. You can resort to some internal convention, such as prefixing with an underscore the name of members you intend as private. However, that’s just a convention, and nothing prevents developers from accessing what the class author considers private.

By using the prototype feature, you can achieve inheritance by simply setting the prototype property of a derived object to an instance of the “parent” object.

Developer = function Developer(name, lastname, birthdate) {
   this.initialize(name, lastname, birthdate);
}
Developer.prototype = new Person();

Be aware that you always need to use this to refer to members of the prototype from within any related member function.

In the prototype model, members are shared by all instances because they are invoked on the shared prototype object. In this way, the amount of memory used by each instance is reduced, which also provides for faster object instantiation. Aside from syntax peculiarities, the prototype model makes defining classes much more similar to the classic object-oriented model than the closure model.

Plain custom objects vs. a hierarchy of classes

The choice between closure and prototype should also be guided by performance considerations and browser capabilities. Prototypes have good load times in all browsers. Closures work great in some browsers (for example, Internet Explorer) and worse in others.

Prototypes provide better support for Microsoft IntelliSense, and they accommodate tool-based statement completion when used in tools that support this feature, such as Microsoft Visual Studio. Prototypes can also help you to obtain type information by simply using reflection. You won’t have to create an instance of the type to query for type information, which is unavoidable if closures are used. Finally, prototypes make it possible for you to easily view private class members when debugging.

So, you have two basic options for dealing with JavaScript objects that look like classes. Prototypes are the option chosen most often by library designers. Also, in jQuery, the prototype property is used extensively.

Having said that, if I had to write client code for a web front end, I’d probably go with jQuery, use a lot of anonymous functions, and not even bother about having a hierarchy of custom objects. I would certainly create custom objects, but I’d use them as plain and flat containers of data and behavior, with no inheritance or polymorphism. If, on the other hand, I had to write my own framework to support some server-side infrastructure, I’d probably opt for a more classic object-oriented approach. In that case, however, I’d probably consider using an existing library instead of creating one of my own. For that, MooTools (http://mootools.net) is an excellent choice.

jQuery’s executive summary

Without beating around the bush, I’ll say that if you’re writing JavaScript code in web views today, you’re likely using jQuery. If you’re not, you should be; the only reasonable scenario I can think for not using jQuery is mobile sites. The jQuery library is certainly not the only JavaScript library you can pick up to save yourself quite a bit of work around DOM manipulation and event handling. However, it’s the world de facto standard. I consider jQuery almost an extension to the JavaScript language, and certainly an extension to the JavaScript skills of any web developers.

This chapter is not the place where you can expect to find some sort of extensive coverage of jQuery. For that, you can pick some good books or just check out the online documentation at http://docs.jquery.com/Main_Page. If you’re looking for online content in a more readable format than dry documentation, go to http://jqfundamentals.com/book.

In this chapter, I provide an overview of the key concepts in jQuery, a strong understanding of which will enable you to quickly grab programming details and features in the library.

DOM queries and wrapped sets

The main reason for the worldwide success of the jQuery library is its unique mix of functional and DOM programming. The library works by selecting DOM elements and applying functions over them, which is just what client web developers need to do most of the time.

The root object

The root of the jQuery library is the jQuery function. Here’s the overall structure of the library:

(
    function( window, undefined )
    {
         var jQuery = (function() {
              // Define a local copy of jQuery
              var jQuery = function(selector, context) {
                 ...
              }
              ...
              return jQuery;
         })();

         /* the rest of the library goes here */
         ...
         window.jQuery = window.$ = jQuery;
    }
) (window);

The nested jQuery function is mapped as an extension to the browser’s window object and is aliased with the popular $ function. The function has the following prototype:

function(selector, context)

The selector indicates the query expression to run over the DOM; the context indicates the portion of the DOM from which to run the query. If no context is specified, the jQuery function looks for DOM elements within the entire page DOM.

The jQuery function typically returns a wrapped set—namely, a collection of DOM elements. Nicely enough, this wrapped set is still a jQuery object that can be queried by using the same syntax, resulting in chained queries.

Running a query

The word query in the library’s name says it all (j stands for JavaScript, but you knew that already)—the jQuery library is primarily designed for running (clever) queries over the DOM and executing operations over the returned items.

The query engine behind the library goes far beyond the simple search capabilities of, for instance, document.getElementById (and related functions) that you find natively in the DOM. The query capabilities of jQuery use the powerful CSS syntax, which gives you a surprising level of expressivity. You find similar query expressivity only in the DOM of HTML5, for which CSS syntax is widely and uniformly supported.

The result of a query is a wrapped set. A wrapped set is an object containing a collection of DOM elements. Elements are added to the collection in the order in which they appear in the original document.

A wrapped set is never null, even if no matching elements have been found. You check the actual size of the wrapped set by looking at the length property of the jQuery object, as shown here:

// Queries for all IMG tags in the page
var wrappedSet = new jQuery("img");
var length = wrappedSet.length;
if (length == 0)
    alert("No IMG tags found.");

Note that the expression just shown, through which you get the wrapped set, is fully equivalent to the more commonly used $(“img”).

The wrapped set is not a special data container. “Wrapped set” is a jQuery-specific term that indicates the results of a query.

Enumerating the content of a wrapped set

To loop through the elements in the wrapped set, you use the each function. This function gets a function as a parameter and invokes that on each element:

// Prints out names of all images
$("img").each(function(index) {
   alert(this.src);
});

The callback function you pass to each receives the 0-based index of the current iteration. Nicely enough, you don’t need to retrieve the corresponding DOM element yourself; you just use the keyword this to refer to the element currently being processed. If the callback function returns false, the iteration is stopped. Be aware that each is a quite generic function made available for any task for which a more specific jQuery function doesn’t exist. If you find a jQuery function that already does what you intend to code through each, by all means use the native function.

You use the length property to read the size of the wrapped set. You can also use the size function, but the length property is slightly faster.

// You better use the length property
alert($("img").size());

The get function extracts the wrapped set from the jQuery object and returns it as a JavaScript array of DOM elements. If you pass an index argument, instead, it will return the DOM element found at the specified 0-based position in the wrapped set.

var firstImage = $("img")[0];

Note that the get function (as well as indexers) breaks the jQuery chainability because it returns a DOM object or an array of DOM objects. You can’t further apply jQuery functions to the results of a get call.

Many more operations are available on wrapped sets, and you can add many others through plug-ins.

Selectors

A query is characterized by a selector. A selector is simply the expression that, when properly evaluated, selects one or more DOM elements. In jQuery, you have three basic types of selectors: selectors based on IDs, cascading style sheets (CSS), or tag names. In addition, a selector can result from the composition of multiple simpler selectors combined by using ad hoc operators. In this case, you have a compound selector.

Basic selectors

An ID selector picks up DOM elements by ID. An ID selector commonly selects only one element unless multiple elements in the page share the same ID—this condition violates the HTML DOM standard, but it’s not too unusual in the real world. Here’s the syntax of an ID selector:

// Select all elements in the context whose ID is Button1
$("#Button1")

The leading # symbol instructs jQuery how to interpret the text that follows it.

A CSS-based selector picks up all elements that share the given CSS class. The syntax is shown here:

// Select all elements in the context styled with the specified CSS class
$(".header")

In this case, the leading dot (.) symbol directs jQuery to interpret the following text as a CSS style name.

Finally, a tag-based selector picks up all elements with the specified tag, such as all <img> tags, all <div> tags, or whatever else you specify. In the following example, the selector consists of the plain tag name—no leading symbol is required:

// Select all IMG elements in the context
$("img")

As mentioned, you can also concatenate two or more selectors to form a more specific one.

Compound selectors

Concatenation is possible through a number of operators. For example, the white space picks up all elements that satisfy the second selector and are descendants of those matching the first. Here’s an example:

// Select all anchors contained within a DIV
$("div a")

The selector just shown is functionally equivalent to the following jQuery expression:

$("div").find("a");

Similar to the white space, the > operator selects elements that are direct child elements (and not just descendants) of the elements matched by the first selector:

// All anchors direct child elements of a DIV
$("div > a")

The preceding selector is functionally equivalent to the following jQuery expression:

$("div").children("a")

Plain concatenation of selectors results in a logical AND of conditions. For example, consider the following query:

$("div.header.highlight")

It selects all <div> elements styled using both the class header and class highlight.

The + operator—the adjacent operator—selects sibling elements in the second selector immediately preceded by elements selected by the first selector. Here’s an example:

// All P immediately preceded by A
$("a + p")

The ~ operator—the next operator—is similar to + except that it selects sibling elements just preceded by others. Here’s an example:

// All P preceded by A
$("a ~ p")

By using the comma, instead, you return the union of elements queried by multiple selectors. In terms of operations, the comma represents a logical OR of selectors. The following example, picks up elements that are either A or P:

// All A and all P
$("a, p")

Beyond simple operators, you have filters. A filter is a jQuery-specific expression that contains some custom logic to further restrict the selected elements.

Predefined filters

You can further refine selectors by applying filters on position, content, attributes, and visibility. A filter is a sort of built-in function applied to the wrapped set returned by a basic selector. Table 11-1 lists positional filters in jQuery.

Table 11-1. Positional filters

Filter

Description

:first

Returns the first DOM element that matches

:last

Returns the last DOM element that matches

:not(selector)

Returns all DOM elements that do not match the specified selector

:even

Returns all DOM elements that occupy an even position in a 0-based indexing

:odd

Returns all DOM elements that occupy an odd position in a 0-based indexing

:eq(index)

Returns the DOM element in the wrapped set that occupies the specified 0-based position

:gt(index)

Returns all DOM elements that occupy a position in a 0-based indexing greater than the specified index

:lt(index)

Returns all DOM elements that occupy a position in a 0-based indexing less than the specified index

:header

Returns all DOM elements that are headers, such as H1, H2, and the like

:animated

Returns all DOM elements that are currently being animated via some functions in the jQuery library

Table 11-2 lists all filters through which you can select elements that are children of a parent element.

Table 11-2. Child filters

Filter

Description

:nth-child(expression)

Returns all child elements of any parent that match the given expression. The expression can be an index or a math sequence (for example, 3n+1), including standard sequences such as odd and even.

:first-child

Returns all elements that are the first child of their parent

:last-child

Returns all elements that are the last child of their parent

:only-child

Returns all elements that are the only child of their parent

A particularly powerful filter is nth-child. It supports a number of input expressions, as shown here:

:nth-child(index)
:nth-child(even)
:nth-child(odd)
:nth-child(expression)

The first format selects the nth child of all HTML elements in the source selector. All child elements placed at any odd or even position in a 0-based indexing are returned if you specify the odd or even filter, instead.

Finally, you can pass the nth-child filter a mathematical sequence expression, such as 3n to indicate all elements in a position that are a multiple of 3. The following selector picks up all rows in a table (labeled Table1) that are at the positions determined by the sequence 3n+1—that is, 1, 4, 7, and so forth:

#Table1 tr:nth-child(3n+1)

Table 11-3 lists expressions used to filter elements by content.

Table 11-3. Content Filters

Filter

Description

:contains(text)

Returns all elements that contain the specified text

:empty

Returns all elements with no children

:has(selector)

Returns all elements that contain at least one element that matches the given selector

:parent

Returns all elements that have at least one child

As far as content filters are concerned, you should note that any text in an HTML element is considered a child node. So, elements selected by the empty filter have no child nodes nor any text. An example is the <br /> tag.

A popular and powerful category of filters are attribute filters. Using attribute filters, you can select HTML elements where a given attribute is in a given relationship with a value. Table 11-4 lists all attribute filters supported in jQuery.

Table 11-4. Attribute filters

Filter

Description

[attribute]

Returns all elements that have the specified attribute. This filter selects the element regardless of the attribute’s value.

[attribute = value]

Returns all elements where the specified attribute is set to the specified value

[attribute != value]

Returns all elements whose specified attribute has a value different from the given one

[attribute ^= value]

Returns all elements whose specified attribute has content that starts with the given value

[attribute $= value]

Returns all elements whose specified attribute has content that ends with the given value

[attribute *= value]

Returns all elements whose specified attribute has content that contains the given value

You can also concatenate attribute filters by simply placing two or more of them side by side, as in the following example:

var elems = $("td[align=right][valign=top]");

The returned set includes all <td> elements for which the horizontal alignment is right and the vertical alignment is top.

The next expression, which is much more sophisticated, demonstrates the power and flexibility of jQuery selectors because it combines quite a few of them:

#Table1 tr:nth-child(3n+1):has(td[align=right]) td:odd

It reads as follows:

Within the body of element Table1, select all <tr> elements at positions 1, 4, 7, and so forth. Next, you keep only table rows for which a <td> element exists with the attribute align equal to the value of right. Furthermore, of the remaining rows, you take only the cells on columns with an odd index.

The result is a wrapped set made of <td> elements.

Finally, a couple more filters exist that are related to the visibility of elements. The :visible filter returns all elements that are currently visible. The :hidden filter returns all elements that are currently hidden from view. The wrapped set also includes all input elements whose type attribute equals “hidden.”

Filter vs. find

To further restrict a query, you can use either the find or filter function on a wrapped set. They are not the same, of course.

The function filter explores the current wrapped set for matching elements and doesn’t ever look into the DOM for descendants. Instead, the function find looks inside of each of the elements in the wrapped set for elements that match the expression. In doing so, however, the function explores the DOM of each element in the wrapped set.

Chaining operations on a wrapped set

The jQuery library offers a wide range of functions that you can apply to the content of a wrapped set. (For a complete list, you can only resort to online documentation or look for an in-depth book.) You can chain function calls because any wrapped set returned by a query is, in turn, another jQuery object that can be further queried. The following expression, for example, works just fine:

$(selector).hide().addClass("hiddenElement");

It first hides from view all matching elements and then adds a specific CSS class to each of them.

You can classify operations that you can perform on wrapped sets in a few groups, as described in Table 11-5.

Table 11-5. Operations on a wrapped set

Effect

Description

DOM manipulation

Creates DOM trees, adds/removes elements, or modifies existing elements

Event binding

Binds and unbinds handlers to events fired by DOM elements

Styling

Applies, removes, or toggles CSS classes to selected elements and gets or sets individual CSS properties

Visibility

Shows and hides DOM elements using transition effects (for example, fading) and duration

In addition, in jQuery you find two other groups of functionalities—cache and Ajax calls—that work with the content of wrapped sets, though they can’t be strictly considered operations available on wrapped sets.

Events

Handling events is a common activity in JavaScript programming. The jQuery library provides a bunch of functions to bind and unbind handlers to events fired by DOM elements.

Binding and unbinding

The bind and unbind pair of functions are used to attach a callback function to the specified event. Here’s an example in which all elements that match the selector will have the same handler attached for the click event:

$(selector).bind("click", function() {
   ...
});

You use the unbind function to detach any currently defined handler for the specified event:

$(selector).unbind("click");

Be aware that the unbind function doesn’t remove handlers that have been inserted directly in the markup through any of the onXXX attributes.

The jQuery library also defines a number of direct functions to bind specific events. Facilities exist for events such as click, change, blur, focus, dblclick, keyup, and so forth. The following code shows how to bind a handler for the click event:

$(selector).click(function() {
   ...
});

Invoked without a callback, the same event functions produce the effect of invoking the current handler, if any are registered. For example, the following code simulates the user clicking a specific button:

$("#Button1").click();

You can achieve the same effect in a more generic way by using the trigger function:

$("#Button1").trigger("click");

Event handlers receive a jQuery internal object—the Event object. This object provides a unified programming interface for events that goes hand in hand with the World Wide Web Consortium (W3C) recommendation, and it resolves discrepancies in the slightly different implementations provided by some browsers:

$("#Button1").click(function(evt) {
    // Access information about the event
    ...

    // Return false if you intend to stop propagation
    return false;
});

The Event object features properties such as mouse coordinates, the JavaScript time of the event, which mouse button was used, and the target element of the event.

Live event binding

Live binding is a nice feature of jQuery by which you can keep track of event bindings for a given subset of DOM elements for the entire page lifetime. In other words, if you opt for live binding instead of plain binding, you are guaranteed that any new dynamically added elements that match the selector will automatically have the same handlers attached. Starting with jQuery 1.7, you should operate live binding through on and off functions. Here’s an example:

$(document).on("click", ".specialButton ", function() {
   ...
})

All buttons decorated with the specialButton CSS style have the given function attached as the handler for the click event. To stop live binding for some elements, you use the off function:

$(".specialButton").off("click");

The difference between using on and bind (or specific event functions such as click) is that when the on function is used, any new DOM elements added to the page and decorated with the specialButton style automatically have the handler added. This won’t happen if bind is used.

Note

If you’re using a version of jQuery older than 1.7, you use live and die methods instead of on and off. The syntax is slightly different because the live method doesn’t take the selector as an argument. Instead, the method applies directly to the selector.

Page and DOM readiness

In the beginning of client-side development, there was just one place where you could put the initialization code of a webpage: in the onload event on either the window object or the <body> tag. The onload event fires as soon as the page has finished loading—that is, after the download of all linked images, CSS styles, and scripts is complete. However, there’s no guarantee that at this time the DOM has been fully initialized and is ready to accept instructions.

The document root object in the DOM exposes a read-only readyState property just to let you know the current state of the DOM and figure out when it’s OK for your page to start scripting it. Using the readyState property is an approach that definitely works, but it’s a bit cumbersome. For this reason, jQuery offers its own ready event that signals when you can start making calls into the framework safely.

<script type="text/javascript">
$(document).ready(
   function() {
     alert("I'm ready!");
   });
</script>

You can have multiple calls to ready in a page or view. When multiple calls to ready are specified, jQuery pushes specified functions to an internal stack and serves them sequentially after the DOM is effectively ready.

The ready event is fired only at the document level; you can’t have it defined for individual elements or any collection of elements in a wrapped set.

Note

The onload event is called after the HTML and any auxiliary resources are loaded. The ready event is called after the DOM is initialized. The two events can run in any order. The onload event won’t ensure that the page DOM is loaded; the ready event won’t ensure that all resources (such as images) have been loaded.

Aspects of JavaScript programming

Today, you often use JavaScript for some client-side logic and input validation. You use JavaScript to download data from remote servers, to implement Windows-like effects such as drag-and-drop, for resizing, for templates, for pop-up and graphic effects, for local data caching, and to manage history and events around the page. It’s used for large chunks of code that have a good level of reusability and need to be safely isolated from one another.

In other words, you want your JavaScript code to be maintainable and unobtrusive.

Unobtrusive code

For years, it has been common to write HTML pages with client buttons explicitly attached to JavaScript event handlers. Here’s a typical example:

<input type="button" value="Click me" onclick="handleClick()" />

From a purely functional perspective, there’s nothing wrong with this code; it works as expected, running the handleClick JavaScript function whenever the user clicks the button. This approach is largely acceptable when JavaScript is just used to spice up webpages; however, it becomes unwieldy when the amount of JavaScript code represents a significant portion of the page or the view.

Style the view by using code

The expression “unobtrusive JavaScript” is popular these days, and it just means that it would be desirable not to have explicit links between HTML elements and JavaScript code. In a way, unobtrusive JavaScript is the script counterpart of CSS classes.

With CSS, you write plain HTML without inline style information and add style to elements by using CSS classes. Likewise, you avoid using event handler attributes (onclick, onchange, onblur, and the like) and use a single JavaScript function to attach handlers when the DOM is ready. Here’s a concise but effective example of unobtrusive JavaScript:

<script type="text/javascript">
    $(document).ready(function () {
        $("#Button1").bind("click", function () {
            var date = new Date().toDateString();
            alert(date);
        });
    });
</script>

<h2>JavaScript Patterns</h2>
<fieldset>
   <legend>#1 :: Click</legend>
   <input type="button" id="Button1" value="Today" />
</fieldset>

You can move the entire <script> block to a separate JavaScript file and have your view be clean and readable.

Pragmatic rules of unobtrusive JavaScript

Unobtrusive JavaScript establishes a fundamental principle—any behavior in any webpage has to be an injectable dependency and not a building block. Rich JavaScript code is made of processing logic and code that manages the user interface (UI). The UI logic needs to know about the DOM and the structure of the view. This necessarily creates a dependency. You can live with such dependencies, but you live better if you work around dependencies.

One way of limiting the impact of UI dependencies is by using templates and ad hoc libraries such as KnockoutJS to render the page. You can learn more about KnockoutJS at http://knockoutjs.com.

Reusable packages and dependencies

More and more pages are extensively based on JavaScript, raising the problem of componentizing more and more the structure of pages. Let’s explore a widely accepted approach for packaging code in JavaScript that has no dependencies on external libraries.

The Namespace pattern

A golden rule of JavaScript programming is grouping related properties—including globals—into containers. When such containers are shared among multiple script files, it might be hard to decide (and enforce) which file has the responsibility of initializing containers and child objects. You just saw an example of a simple syntax that you can use to define a global container.

var GLOBALS = GLOBALS || {};

This trick works, but it’s often too cumbersome to use when you have several nested objects that you need to manage. Here’s where the Namespace pattern comes to the rescue.

The Namespace pattern consists of a piece of code that iterates over the tokens of a dot-separated string (for example, a C# or Java namespace) and ensures that the proper hierarchy of objects is initialized. The namespace function ensures that the code is not being destructive and skips over existing instances. Here’s some sample code:

var GLOBALS = GLOBALS || {};

GLOBALS.namespace = function (ns) {
    var objects = ns.split("."),
            parent = GLOBALS,
            startIndex = 0,
            i;

    // You have one GLOBALS object per app. This object already exists if you
    // can call this function. So you can safely ignore the root of the namespace
    // if it matches the parent string.
    if (objects[0] === "GLOBALS")
        startIndex = 1;

    // Create missing objects in the namespace string
    for (i = startIndex; i < objects.length; i++) {
        var name = objects[startIndex];
        if (typeof parent[name] === "undefined")
            parent[name] = {};
        parent = parent[name];
    }
    return parent;
};

After you have referenced the namespace function, you can then place the following calls:

GLOBALS.namespace("Widgets");                     // GLOBALS has a Widgets property
GLOBALS.namespace("GLOBALS.Widgets");             // GLOBALS has a Widgets property

These two calls are equivalent, and both guarantee that GLOBALS has an initialized Widgets property. Consider the following:

// GLOBALS has a Widgets property, and Widgets has a NewsBox property
GLOBALS.namespace("GLOBALS.Widgets.NewsBox");

The namespace function proceeds iteratively and also ensures that Widgets has an NewsBox property.

Important

Although the implementation shown here can be considered relatively standard, I feel obliged to credit Stoyan Stefanov for inspiring this code and the section on the Module pattern. Stoyan is the author of the excellent book JavaScript Patterns (O’Reilly Media, 2010). I recommend it to anybody who wants to go beyond the content of this chapter.

The Module pattern

The Module pattern provides a way to package self-contained blocks of code that you can simply add or remove from a project. The pattern wraps a classic JavaScript function into an immediate function that guarantees privacy of data and ensures that only what you explicitly reveal as public is actually perceived as public by clients. In JavaScript, an immediate function is a function defined inline and immediately executed. The syntax is shown here:

(
   function(...) {
     // Body
   } (...)
);

Let’s use the Module pattern to build a self-contained widget that grabs news from a given feed. Suppose that you save all of the following code to a JavaScript file:

GLOBALS.namespace("Widgets.News");
GLOBALS.Widgets.News = function () {
    var localUrl = "...",
        localWidget = "",
        localBuildWidget = function (items) {
            var numOfNews = items.length;
            if (localSettings.maxNews >0)
                 numOfNews = Math.min(localSettings.maxNews, numOfNews);

            var buffer = "<table rules='rows'>";
            for (var i = 0; i < numOfNews; i++) {
                buffer += "<tr><td>" + items[i].Title + "</td></tr>";
            }
            buffer += "</table>";
            localWidget = buffer;
        },
        localSettings = {
            maxNews: 5,
            autoRefreshEvery: 0
        };

    return {
        load: function (selector, settings) {
            if (settings != null)
                localSettings = settings;

            $.getJSON(localUrl)
                .done(function (data) {
                    localBuildWidget(data);
                    $(selector).html(localWidget);
                });
        },
        getHtml: function () {
            return localWidget;
        }
    };
} ();

The code defines a logical block that contains all the logic required to download and format a bunch of news from a feed.

With the Module pattern, you return an object that reveals the public API that you want to make visible. Here’s how you use a module:

<script type="text/javascript" src="@Url.Content("~/content/scripts/module-news.js")"></script>
<script type="text/javascript">
    GLOBALS.Widgets.News.load("#twitter-box", {maxNews: 10});
</script>

You first link the distinct file that contains the widget and then initialize it, passing settings as appropriate. The widget in this case gets the jQuery selector of the UI where it will make graphical changes—specifically, where the widget will insert the HTML table with the list of selected news.

The Namespace pattern is not necessary for the implementation of the Module pattern, but it helps a lot to have it.

Script and resource loading

More and more script in webpages means more and more script files to download. This might soon become a serious issue and needs to be addressed. When a page has several scripts, the degree of parallelism at which the browser can operate is dramatically lowered. So it is for the load time of the page. Let’s see why.

The download is always synchronous

The HTTP/1.1 specification suggests that browsers download no more than two components in parallel per host name. However, that never happens for script files: browsers always download script files synchronously and one at a time. As a result, the total download time is at least the sum of times required to download individual files and, maybe worse, the browser is idle while downloading a script file. Page rendering resumes only after the script files have been downloaded, parsed, and executed.

Browsers implement synchronous downloads mostly to stay on the safe side. In fact, there’s always the possibility that script files include instructions such as JavaScript immediate functions or document.write that could modify the status of the current DOM.

Scripts at the bottom

To improve the page-loading performance, you can use a simple trick that consists of moving all links to script files to the bottom of the page just before the </body> tag. When you do this, browsers don’t need to interrupt the page rendering process to load scripts. Browsers can then do their best to display an early view of the page.

Although placing manual scripts at the bottom is the safest approach, another option exists that you can set up declaratively and without resorting to writing or importing ad hoc JavaScript code: the defer attribute.

<script src="..." defer="defer"></script>

Introduced with the HTML 4 specification, the defer attribute instructs the browser whether loading the script can be deferred to the end of the page processing. A <script> tag decorated with the defer attribute implicitly states that it’s not doing any direct document writing and it’s safe for it to be loaded at the end. The purpose of the defer attribute is similar to the async attribute you find in the HTML5 specification.

Dealing with static files

A golden rule of web development states that after you’ve neutralized the performance hit of static files such as scripts, style sheets, and images, you’re pretty much done with optimization. There are two main ways to minimize the download time of static resources, and one doesn’t exclude the other.

The most obvious trick is reducing the size of the files being downloaded. The second most obvious trick consists of not downloading them at all. Before I get into the details, let me state up front that these optimization tricks should be played when you’re done with development. If they’re applied at development time, most of them only add frustration and slow down your progress.

Reducing the size of downloadable files means compressing their content. Browsers inform the web server about the type of compression they support through the Accept-Encoding header. Chapter 8, demonstrates that in ASP.NET MVC you can add a proper response header to any controller action and instruct the web server to use any of the supported encodings for the response. Compression can also be turned on for static resources directly at the web-server level. It should be noted that you should not use GZIP compression (or perhaps the deflate compression) on files (and responses) that are already compressed on their own. For example, you don’t want to use GZIP for a JPEG image. Although GZIP compression can cut the size by 50 percent or so, it gives you much less impressive results on sources that are already compressed and at the cost of more CPU work.

The second aspect to consider is browser caching. Static resources are said to be static just because they don’t change frequently. So, why should you download them over and over again? By assigning your static resources a very long duration (through the Expires response header), you save the browser from downloading these resources frequently. Again, you can do that at the web-server level for static resources and programmatically via the Response object for dynamically served resources.

In this regard, a content delivery network (CDN) is beneficial because it increases the likelihood that the browser cache already contains a resource that might have been referenced by using the same URL by other sites using the same CDN. Be aware that you won’t benefit much from placing on a CDN files that only one application uses.

Note

Yet another option to neutralize the costs of static resources is putting those resources on a different server that is optimized to return static content. Or, perhaps you can use a reverse proxy tool such as Varnish (http://www.varnish-cache.org) to collect images from various servers and return them as if they originated from the proxy. By having a reverse proxy in front of the CDN or the website, you can get significant benefits in terms of reduced requests for static files.

Using sprites

To improve the serving of images, you can consider using sprites. A sprite is a single image that results from the composition of multiple images. Constituent images are saved side by side in the process of forming a new image, as shown in Figure 11-1.

A composed image served as a sprite.
Figure 11-1. A composed image served as a sprite.

In pages, you use the <img> tag to reference the total image and resort to using a specific CSS style to select the portion of it in which you’re interested. For example, here’s the CSS you need to use for the segments of the image just shown to render a clickable button:

.signup-link
{
    width: 175px;
    height: 56px;
    background-image: url(/images/loginsprite.png);
    background-position: -0px 0px;
    background-repeat: no-repeat;
}

.signup-link:hover
{
    background-position: -177px 0px;
}

.signup-link:active
{
    background-position: -354px 0px;
}

The background-position indicates the relative position at which to start rendering the image. For example, when the mouse hovers over the image, the browsers begins rendering skipping the initial 177 pixels. This means that the first button is not displayed and the section of the sprite that is rendered coincides with the highlighted button. (As you can see in Figure 11-1, for clarity, I added a blank line of pixels, which is why you have to skip one pixel when counting positions.) The net effect is that you have just one image, just one download, a cached image, and a cool effect for users. (See Figure 11-2.)

Sprites in action.
Figure 11-2. Sprites in action.

Bundling and minification

As webpages continue to offer ever-richer visual content, the cost of downloading related resources such as CSS, scripts, and images grows significantly. Surely, for the most part these resources might be cached locally by the browser; yet the initial footprint can really be hard to sustain. For script and CSS files, GZIP compression can be combined with bundling and minification.

Bundling is the process of rolling up a number of distinct resources together into a single downloadable resource. For example, a bundle might consist of multiple JavaScript or CSS files. Minification is a transformation applied to an individual resource. In particular, minification consists of removing all unnecessary characters from a text-based resource in a way that doesn’t alter the expected functionality. This means removing comments, white space characters and new lines; in general all characters that are usually added for readability but take up space and do not really serve any functional purposes.

You can apply bundling and minification together, but they remain independent processes. Depending on the needs, you can decide to only create bundles or minify individual files. Usually, however, there are no reasons on production sites not to bundle and minify all CSS and JavaScript files. At debug time, though, it’s an entirely different story: a minified or bundled resource is quite hard to read and step through, so you just don’t want bundling and minification enabled.

There are a lot of frameworks out there that provide bundling and minification services with slightly different levels of extensibility and different feature sets. I dare say that for the most part they all offer the same capabilities; so, picking one over the other is purely a matter of preference. If you’re writing an ASP.NET MVC application, the natural choice for bundling and minification is the Microsoft ASP.NET Web Optimization framework, available through a NuGet package.

Typically, you create bundles programmatically in global.asax. In accordance with the ASP.NET MVC conventions, you create a BundleConfig class in the App_Start folder and expose a static initialization method out of it, as shown here:

BundleConfig.RegisterBundles(BundleTable.Bundles);

A bundle is simply a collection of files (typically style sheets or script files). Here’s the code you need to group two CSS files into a single download:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new Bundle("~/all-css").Include(
               "~/content/styles/site1.css",
               "~/content/styles/site2.css"));

        BundleTable.EnableOptimizations = true;
    }
}

You create a new Bundle class and pass to the constructor the virtual path that will be used to reference the bundle from within a view. To associate CSS files with the bundle, you use the Include method. The method takes an array of strings representing virtual paths. You can indicate CSS files explicitly, as in the preceding example, or you can indicate a pattern string, as demonstrated in the following:

bundles.Add(new Bundle("~/all-css").Include("~/content/styles/*.css");

Bundling is a form of optimization; as such, it mostly makes sense when the site is in production. The EnableOptimization property is a convenient way to set up bundling as it should work in production. Be aware that until it is turned on explicitly, bundling is not active.

Bundling script files

Bundle classes can work with CSS or JavaScript files without difference. However, the BundleCollection class has a couple of features that are mostly useful when bundling script files: ordering and ignore lists.

The BundleCollection class has a property named Orderer of type IBundleOrderer. As obvious as it might seem, an orderer is a component responsible for determining the actual order in which you want files to be bundled for download. The default orderer is the DefaultBundleOrderer class. This class bundles files in the order that results from the settings set through the FileSetOrderList property, which is another property of BundleCollection. The FileSetOrderList property is designed to be a collection of BundleFileSetOrdering classes. Each of these classes defines a pattern for files (for example, jquery-*) and the order of BundleFileSetOrdering instances determines the actual order of files in the bundle. For example, given the default configuration, all jQuery files are always bundled before Modernizr files. Orderings for common groups of files (such as jQuery, jQuery UI and Modernizr) are predefined; you can programmatically reset and update orderings at will.

Note

The impact of the DefaultBundleOrderer class on CSS files is more limited but not null. If you have a reset.css and/or a normalize.css file in your website, these files are automatically bundled before any of your other CSS files, and reset.css always precedes normalize.css. The goal of having reset/normalize style sheets is to provide a standard set of style attributes for all HTML (reset) and HTML5 (normalize) elements so that your pages don’t inherit browser-specific settings such as fonts, sizes, margins. Although some recommended content exists for both CSS files, the actual content is up to you. If you have files with these names in your project, ASP.NET MVC makes an extra effort to ensure that they are bundled before anything else.

If you want to override the default orderer and ignore predefined bundle file set orderings, you have two options. First, you can create your own orderer which works on a per-bundle basis. Here’s an example that just ignores predefined orderings:

public class SimpleOrderer : IBundleOrderer
{
    public IEnumerable<FileInfo> OrderFiles(
           BundleContext context, IEnumerable<FileInfo> files)
    {
        return files;
    }
}

You use it as illustrated here:

var bundle = new Bundle("~/all-css");
bundle.Orderer = new SimpleOrderer();

In addition, you can reset all orderings by using the following code:

bundles.ResetAll();

In this case, the effect of using the default orderer or the simple orderer shown earlier is the same. However, be aware that ResetAll also resets all current script orderings.

The second noteworthy feature is the ignore list. Defined through the IgnoreList property of the BundleCollection class, it defines the pattern matching strings for files that were selected for inclusion in the bundle, but should be ignored instead. The major benefit of ignore lists is you can specify *.js in the bundle, but you can use them to skip over, for instance, *.vsdoc.js files. The default configuration for IgnoreList takes care of most common scenarios (including *.vsdoc.js files) while giving you a chance to customize.

Adding minification

The Bundle class is only concerned with packing multiple resources together so that they are captured in a single download and cached. However, both style sheets and script files are padded with blanks and newline characters for readability purposes. Readability is important for humans (and then at debug time) but is never an issue for browsers. The string below is a sample minified version CSS perfectly acceptable for a browser. As you can see, it doesn’t include any extra characters.

html,body{font-family:'segoe ui';font-size:1.5em;}html,body{background-color:#111;color:#48d1cc}

How would you add minification to CSS and JavaScript files? It is as simple as changing the Bundle class with StyleBundle or ScriptBundle class. Both classes are surprisingly simple: They inherit from Bundle and just consist of a different constructor.

public ScriptBundle(string virtualPath)
         : base(virtualPath, new IBundleTransform[] { new JsMinify() })
{
}

The Bundle class has a constructor that accepts a list of IBundleTransform objects. These transforms are just applied one after the next to the content. The ScriptBundle class just adds the JsMinify transformer. The StyleBundle class adds instead the CssMinify transformer. CssMinify and JsMinify are the default minifiers for ASP.NET MVC 4 and are based on the WebGrease framework. Needless to say, if you want to switch to a different minifier, all you need to do is to create the class—an implementation of IBundleTransform—and pass it via the constructor.

Summary

People like to consume interactive applications through the web. For various reasons, the most common way of writing these applications is still JavaScript. A die-hard language, JavaScript happily survived the advent of Adobe Flash and Microsoft Silverlight. Although Flash and Silverlight are still used in some web applications, they currently have no chance to cannibalize JavaScript.

JavaScript was originally introduced to give web authors the ability to incorporate some simple logic and action in HTML pages. JavaScript was not designed to be a cutting-edge programming language. The design of JavaScript was influenced by many languages, but the predominant factor was simplicity. It needs extra facilities to support the development of any functionality that goes beyond changing the attribute of a DOM element, which is where libraries such as the de facto standard jQuery as well as KnockoutJS and AngularJS fit in.

In Chapter 12, we step into the development of a type of client-side, JavaScript-intensive application: single page applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.242.165