Manipulating JavaScript objects and collections

The majority of jQuery features implemented as utility functions are designed to operate on JavaScript objects other than the DOM elements. Generally, anything designed to operate on the DOM is provided as a jQuery method. Although some of these functions can be used to operate on DOM elements—which are JavaScript objects, after all—the focus of the utility functions isn’t DOM-centric.

These functions run the gamut from simple string manipulation and type testing to complex collection filtering, serialization of form values, and even implementing a form of object inheritance through property merging. Let’s start with one that’s pretty basic.

Trimming strings

Almost inexplicably, before ECMAScript 5 the String type didn’t possess a method to remove whitespace characters from the beginning and end of a string instance. Such basic functionality is part of a String class in most other languages, but JavaScript mysteriously lacked this useful feature until a few versions ago. What this means is that you can’t use this function in versions of Internet Explorer prior to 9.

String trimming is a common need in many JavaScript applications; one prominent example is during form data validation. Because whitespace is invisible on the screen (hence its name), it’s easy for users to accidentally enter extra space characters before or after valid entries in text boxes or text areas. During validation, you want to silently trim such whitespace from the data rather than alerting the user to the fact that something they can’t see is tripping them up.

To help you out with older browsers, but also to help people with all browsers prior to the introduction of the JavaScript native method (String.prototype.trim()), the jQuery team included the $.trim() utility function. Behind the scenes, to improve its performance this method uses the native String.prototype.trim() where supported. The $.trim() method is defined as follows.

Function syntax: $.trim
$.trim(value)
Removes any leading or trailing whitespace characters from the passed string and returns the result. Whitespace characters are defined in this case as any character matching the JavaScript regular expression \s, which matches not only the space character but also the form feed, new line, return, tab, and vertical tab characters, as well as the Unicode character \u00A0.
Parameters
value(String) The string value to be trimmed. This original value isn’t modified.
Returns
The trimmed string.

A simple example of using this function to trim the value of a text field is

var trimmedString = $.trim($('#some-field').val());

Be aware that this function converts the parameter you pass to its String type equivalent, so if you erroneously pass an object to it, you’ll obtain the string "[object Object]".

Now let’s look at some functions that operate on arrays and other objects.

Iterating through properties and collections

Oftentimes when you have nonscalar values composed of other components you’ll need to iterate over the contained items. Whether the container element is a JavaScript array (containing any number of other JavaScript values, including other arrays) or instances of JavaScript objects (containing properties), the JavaScript language gives you the means to iterate over them. For arrays, you iterate over their elements using the for loop; for objects, you iterate over their properties using the for...in loop (other constructs are available, but let’s ignore them for the moment).

You can code examples of each as follows:

var anArray = ['one', 'two', 'three'];
for (var i = 0; i < anArray.length; i++) {
  // Do something here with anArray[i]
}
var anObject = {one: 1, two: 2, three: 3};
for (var prop in anObject) {
  // Do something here with prop
}

Pretty easy stuff, but some might think that the syntax is needlessly wordy and complex—a criticism frequently targeted at the for loop.

A few years ago, a method of the Array object called forEach() was added to JavaScript. Unfortunately, being a later addition, some browsers, most notably versions of Internet Explorer prior to 9, don’t support it. In addition, belonging to the Array object, it can’t be employed to iterate over other kinds of objects. jQuery to the rescue!

You know that jQuery defines the each() method, allowing you to easily iterate over the elements in a jQuery collection without the need for the for loop syntax. For arrays, array-like objects, and objects, jQuery provides an analogous utility function named $.each().

The really nice thing is that the same syntax is used, whether iterating over the items in an array or the properties of an object. Besides, it can be used in Internet Explorer 6–8 as well. Its syntax is as follows.

Function syntax: $.each
$.each(collection, callback)
A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function’s arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.
Parameters
collection(Array|Object) An array (or array-like object) whose items are to be iterated over, or an object whose properties are to be iterated over.
callback(Function) A function invoked for each element in the collection. If the collection is an array (or array-like object), this callback is invoked for each array item; if it’s an object, the callback is invoked for each object property. The first parameter to this callback is the index of the array element or the name of the object property. The second parameter is the array item or property value. The function context (this) of the invocation is also set to the value passed as the second parameter.
Returns
The same collection passed.

This unified syntax can be used to iterate over arrays, array-like objects, or objects using the same format. With this function, you can write the previous example as follows:

var anArray = ['one', 'two', 'three'];
$.each(anArray, function(i, value) {
  // Do something here
});
var anObject = {one:1, two:2, three:3};
$.each(anObject, function(name, value) {
  // Do something here
});

Although using $.each() with an inline function sounds good, this function makes it easy to write reusable iterator functions or to factor out the body of a loop into another function for purposes of code clarity, as in the following example:

$.each(anArray, someComplexFunction);

Note that when iterating over a collection, you can break out of the loop by returning false from the iterator function. On the contrary, returning a truthy value (values evaluating to true) is the same as using continue, which means that the function stops immediately and the next iteration is performed.

jQuery 3: Feature added

jQuery 3 introduces the ability to iterate over the DOM elements of a jQuery collection using the for-of loop, part of the ECMAScript 6 specifications. Thanks to this feature, you can now write code like the following:

var $divs = $('div');
for (var element of $divs) {
   // Do something with element
}

Please note how we didn’t prepend the dollar sign in front of the element variable to highlight that its value will be a DOM element and not a jQuery collection made of one element at a time.

Note

Using the $.each() function may be convenient from a syntax point of view, but it’s usually (slightly) slower than using the old-fashioned for loop. Whether you should use it or not is really up to you.

Sometimes you may iterate over arrays to pick and choose elements to become part of a new array. Although you could use $.each() for that purpose, let’s see how jQuery makes that even easier.

Filtering arrays

Traversing an array to find elements that match certain criteria is a frequent need of applications that handle lots of data. You might wish to filter the data for items that fall above or below a particular threshold, or, perhaps, that match a certain pattern. For any filtering operation of this type, jQuery provides the $.grep()utility function.

The name of the $.grep() function might lead you to believe that the function employs the use of regular expressions like its namesake UNIX grep command. But the filtering criterion used by the $.grep() utility function isn’t a regular expression; it’s a callback function provided by the caller that defines the criteria to determine whether a data value should be included or excluded from the resulting set of values. Nothing prevents that callback from using regular expressions to accomplish its task, but their use isn’t automatic.

The syntax of the function is as follows.

Function syntax: $.grep
$.grep(array, callback[, invert])
Traverses the passed array, invoking the callback function for each value. The return value of the callback function determines whether the value is collected into a new array returned as the value of the $.grep() function. If the invert parameter is omitted or false, a callback value of true causes the data to be collected. If invert is true, a callback value of false causes the value to be collected. The original array isn’t modified.
Parameters
array(Array) The traversed array whose data values are examined for collection. This array isn’t modified in any way by this operation.
callback(Function) A function whose return value determines whether the current data value is to be collected. This function receives two parameters: the current value for this iteration and the index of the value within the array. A return value of true causes the current value to be collected, unless the value of the invert parameter is true, in which case the opposite occurs.
invert(Boolean) An optional value that if true inverts the normal operation of the function.
Returns
The array of collected values.

Let’s say that you want to filter an array for all values that are greater than 100. You’d do that with a statement such as the following:

var bigNumbers = $.grep(originalArray, function(value) {
                    return value > 100;
                 });

The callback function that you pass to $.grep() can use whatever processing it likes to determine if the value should be included. The decision could be as easy or as complex as you need.

Even though the $.grep() function doesn’t directly use regular expressions (despite its name), JavaScript regular expressions can be powerful tools in your callback functions to determine whether to include or exclude values from the resultant array. Consider a situation in which you have an array of values and wish to identify any values that don’t match the pattern for United States postal codes (also known as ZIP codes).

U.S. postal codes consist of five decimal digits optionally followed by a dash and four more decimal digits. A regular expression for such a pattern would be /^\d{5} (-\d{4})?$/, so you could filter a source array for nonconformant entries with the following:

var badZips = $.grep(
                  originalArray,
                  function(value) {
                    return value.match(/^\d{5}(-\d{4})?$/) !== null;
                  },
                  true
              );

Notable in this example is the use of the String class’s match() method to determine whether a value matches the pattern or not and the specification of the invert parameter to $.grep() as true to exclude any values that match the pattern.

Collecting subsets of data isn’t the only operation you might perform on them. Let’s look at another utility function that jQuery provides.

Translating arrays

Data might not always be in the format that you need it to be. Another common operation that’s frequently performed in data-centric web applications is the translation of a set of values to another set. Although it’s a simple matter to write a for loop to create one array from another, or an array from an object, jQuery makes it even easier with the $.map utility function.

Function syntax: $.map
$.map(collection, callback)
Iterates through the passed array or object, invoking the callback function for each item and collecting the return values of the function invocations in a new array.
Parameters
collection(Array|Object) An array or object whose values are to be transformed to values in the new array.
callback(Function) A function whose return values are collected in the new array returned as the result of a call to the $.map() function. This function is passed two parameters: the current value and the index of that value within the original array. If an object is passed, the second argument is the property name of the current value.
Returns
The array of collected values.

Let’s look at a trivial example that shows the $.map() function in action:

var oneBased = $.map(
   [0, 1, 2, 3, 4],
   function(value) {
      return value + 1;
   }
);

This statement converts the passed array into the following:

[1, 2, 3, 4, 5]

An important behavior to note is that if the function returns either null or undefined, the result isn’t collected. In such cases, the resulting array will be smaller in length than the original, and the one-to-one correspondence between items by order is lost.

Let’s now look at a slightly more involved example. Imagine that you have an array of strings, perhaps collected from form fields, that is expected to represent numeric values. You want to convert this array of strings to an array of corresponding Number instances. Because there’s no guarantee against the presence of an invalid numeric string, you need to take some precautions. Consider the following code, which is also available in the file lesson-9/$.map.html and as a JS Bin:

var strings = ['1', '2', '3', '4', 'S', '6'];
var values = $.map(strings, function(value) {
   var result = new Number(value);
   return isNaN(result) ? null : result;
});

You start with an array of string values, each of which is expected to represent a numeric value. But a typo (or perhaps user entry error) resulted in the letter S instead of the expected number 5. The code handles this case by checking the Number instance created by the constructor to see if the conversion from string to numeric was successful or not. If the conversion fails, the value returned will be the constant NaN. But the funny thing about NaN is that, by definition, it doesn’t equal anything else, including itself! Therefore the value of the expression NaN===NaN is false!

Because you can’t use a comparison operator to test for NaN (which stands for Not a Number, by the way), JavaScript provides the isNaN() method, which you employ to test the result of the string-to-numeric conversion.

In this example, you return null in the case of failure, ensuring that the resulting array contains only the valid numeric values with any error values elided. If you want to collect all the values, you can allow the transformation function to return NaN for bad values.

Another useful behavior of $.map() is that it gracefully handles the case where an array is returned from the transformation function, merging the returned value into the resulting array. Consider the following statement:

var characters = $.map(
   ['this', 'that'],
   function(value) {
      return value.split('');
   }
);

This statement transforms an array of strings into an array of all the characters that make up the strings. After execution, the value of the variable characters is as follows:

['t', 'h', 'i', 's', 't', 'h', 'a', 't']

This is accomplished by use of JavaScript’s split() method, which returns an array of the string’s characters when passed an empty string as its delimiter. This array is returned as the result of the transformation function and is merged into the resultant array.

jQuery’s support for arrays doesn’t stop here. There are a handful of minor functions that you might find handy.

More fun with JavaScript arrays

Have you ever needed to know if a JavaScript array contained a specific value and, perhaps, even the location of that value in the array? If so, you’ll appreciate the $.inArray() function.

Function syntax: $.inArray
$.inArray(value, array[, fromIndex])
Returns the index position of the first occurrence of the passed value.
Parameters
value(Any) The value for which the array will be searched.
array(Array) The array to be searched.
fromIndex(Number) The index of the array at which to begin the search. The default is 0, which will search the whole array.
Returns
The index of the first occurrence of the value within the array, or -1 if the value isn’t found.

A trivial but illustrative example of using this function is

var index = $.inArray(2, [1, 2, 3, 4, 5]);

This results in the index value of 1 being assigned to the index variable.

Another useful array-related function creates JavaScript arrays from other array-like objects. Consider the following snippet:

var images = document.getElementsByTagName('img');

This populates the variable images with a HTMLCollection of all the images on the page.

Dealing with this and similar objects is a bit of a pain because they lack native JavaScript Array methods like sort() and indexOf(). Converting a HTMLCollection (and similar objects) to a JavaScript array makes things a lot nicer. The jQuery $.makeArray function is what you need in this case.

Function syntax: $.makeArray
$.makeArray(object)
Converts the passed array-like object into a JavaScript array
Parameters
object(Object) Any object to turn into a native Array
Returns
The resulting JavaScript array

This function is intended for use in code that makes little use of jQuery, which internally handles this sort of thing on your behalf. This function also comes in handy when handling the arguments array-like object within functions. Imagine that you have the following function and that internally you want to sort the arguments provided to it:

function foo(a, b) {
   // Sort arguments here
}

You can grab all the arguments at once using the arguments array-like. The problem is that arguments is not of type Array, so you can’t write:

function foo(a, b) {
   var sortedArgs = arguments.sort();
}

This code will throw an error because arguments doesn’t possess the JavaScript Array’s sort() method that would be useful to sort the arguments. This is a situation where the $.makeArray() can help. You can fix the issue by converting arguments into a real array and then sort its elements:

function foo(a, b) {
   var sortedArgs = $.makeArray(arguments).sort();
}

Once you make this change, sortedArgs will contain an array with the arguments passed to the foo() function sorted as required.

Let’s now say that you have the following statement:

var arr = $.makeArray({a: 1, b: 2});

Once this statement is executed, arr will contain an array made of one element, which is the object passed as an argument to $.makeArray().

Another seldom-used function that might be useful when dealing with arrays built outside of jQuery is the $.unique() function.

Function syntax: $.unique
$.unique(array)
Given an array of DOM elements, returns an array of the unique elements in the original array, sorted in document order
Parameters
array(Array) The array of DOM elements to be examined
Returns
An array of DOM elements returned in document order, consisting of the unique elements in the passed array

This function is intended for use on element arrays made of DOM elements created outside the bounds of jQuery. Although many people think that this function can be used with arrays of strings or numbers, we want to stress that $.unique() works only with arrays of DOM elements.

Before delving into the description of the next function, let’s look at an example of using $.unique(). Consider the following markup:

<div class="black">foo</div>
<div class="red">bar</div>
<div class="black">baz</div>
<div class="red">don</div>
<div class="red">wow</div>

Now imagine that for some reason you need to retrieve the <div>s having class black as an array of DOM elements, then add all the <div>s in the page to this collection, and finally filter the latter to remove the duplicates. You can achieve these requirements with the following code (which includes some statements to log the difference in the number of elements):

var blackDivs = $('.black').get();
console.log('The black divs are: ' + blackDivs.length);
var allDivs = blackDivs.concat($('div').get());
console.log('The incremented divs are: ' + allDivs.length);
var uniqueDivs = $.unique(allDivs);
console.log('The unique divs are: ' + uniqueDivs.length);

In case you want to play with this example, you can find it in the file lesson-9/$.unique.html and online as a JS Bin.

jQuery 3: Method renamed

jQuery 3 renamed the $.unique() utility function in $.uniqueSort() to make it clear that the function sorts as well. Despite this change, in jQuery 3 you can still invoke $.unique(), which is now just an alias for $.uniqueSort(), but it’s now deprecated and will be removed in following versions of the library.

Now suppose that you want to merge two arrays. jQuery has a function for this task called $.merge.

Function syntax: $.merge
$.merge(array1, array2)
Merges the values of the second array into the first and returns the result. The first array is modified by this operation and returned as the result. The second array isn’t modified.
Parameters
array1(Array) An array into which the other array’s values will be merged.
array2(Array) An array whose values will be merged (concatenated) into the first array.
Returns
The first array, modified with the results of the merge.

Consider the following code:

var arr1 = [1, 2, 3, 4, 5];
var arr2 = [5, 6, 7, 8, 9];
var arr3 = $.merge(arr1, arr2);

After this code executes, arr2 is untouched, but arr1 and arr3 contain the following:

[1, 2, 3, 4, 5, 5, 6, 7, 8, 9]

Note how there are two occurrences of 5 because the $.merge() utility function doesn’t remove the duplicates.

Now that you’ve seen how jQuery helps you to easily work with arrays, let’s see how it helps you manipulate plain-old JavaScript objects.

Extending objects

Although we all know that JavaScript provides some features that make it act in many ways like an object-oriented language, JavaScript isn’t what anyone would call purely object-oriented because of the features that it doesn’t support. One of these important features is inheritance (supported in the ECMAScript 6)—the manner in which new classes are defined by extending the definitions of existing classes.

A pattern for mimicking inheritance in JavaScript is to extend an object by copying the properties of a base object into the new object, extending the new object with the capabilities of the base.

It’s fairly easy to write JavaScript code to perform this extension by copying, but as with so many other procedures, jQuery anticipates this need and provides a ready-made utility function to help you out: $.extend(). As you’ll see in lesson 12, which is about jQuery plugins, this function is useful for much more than extending an object. Its syntax is as follows.

Function syntax: $.extend
$.extend([deep,] target, [source1, source2, … sourceN])
Extends the object passed as target with the properties of the remaining passed objects. The extended object is also provided as the return value.
Parameters
deep(Boolean) An optional flag that determines whether a deep or shallow copy is made. If omitted or false, a shallow copy is executed. If true, a deep copy is performed.
target(Object) The object whose properties are augmented with the properties of the source object(s). If this object is the only parameter provided, it’s assumed as a source and the jQuery object is assumed to be the target. If more than one argument is given, this object is directly modified with the new properties before being returned as the value of the function. Any properties with the same name as properties in any of the source elements are overridden with the values from the source elements.
source1 …
sourceN
(Object) One or more objects whose properties are added to the target object. When more than one source is provided and properties with the same names exist in the sources, sources later in the argument list override those earlier in the list.
Returns
The extended target object.

The behavior of this function is interesting because of its flexibility. Almost every argument in the signature is optional and allows you to change what the function does. Arguments that are null or undefined are ignored.

If only one object is provided, then it’s interpreted not as a target but as a source. In this case, the jQuery object is assumed to be the target and properties of the object are merged into the jQuery object.

It’s worth noting that the merged object may have fewer properties than the sum of those of the target and the sources even if they’re all different. The reason is that $.extend() ignores the properties whose value is undefined. Let’s take a look at this function with an example. You’ll set up three objects, a target and two sources, as follows:

var target = {a: 1, b: 2, c: 3};
var source1 = {c: 4, d: 5, e: 6};
var source2 = {c: 7, e: 8, f: 9};

Then you’ll operate on these objects using $.extend() as follows:

$.extend(target, source1, source2);

This code takes the contents of the source objects and merges them into the target. To test this code, we’ve set up this example code in the file lesson-9/$.extend.test.1.html, which executes the code and displays the results on the page. Loading this page into a browser results in the display shown in figure 9.3.

Figure 9.3. The $.extend() function merges properties from multiple source objects without duplicates and gives precedence to instances in reverse order of specification.

As you can see, all properties of the source objects have been merged into the target object. But note the following important nuances:

  • All of the objects contain a property named c. The value of c in source1 replaces the value in the original target, which in turn is replaced by the value of c in source2.
  • Both source1 and source2 contain a property named e. The value of e within source2 overrides the value within source1 when merged into target, demonstrating how objects later in the list of arguments take precedence over those earlier in the list.

Before moving forward, let’s look at another example of a situation that you, like your dear authors, have faced several times. Let’s say that you want to merge the properties of two objects preserving both of them, which means that you don’t want the target object to be modified. To perform this operation, you can pass an empty object as the target, as shown here:

var mergedObject = $.extend({}, object1, object2);

By passing an empty object as the first parameter, both object1 and object2 are treated as sources; thus they’re not modified.

As the last example, we’ll show you the result of performing a deep copy of two objects using the first parameter of $.extend(). Let’s say that you have the following objects:

var target =  {a: 1, b: 2};
var source1 = {b: {foo: 'bar'}, c: 3};
var source2 = {b: {hello: 'world'}, d: 4};

You operate on these objects calling the $.extend() method as follows:

$.extend(true, target, source1, source2);

Once again, we’ve created a page that reproduces this example so that you can play with it. The code is contained in the file lesson-9/$.extend.test.2.html. Loading the latter into your browser will give you the results shown in figure 9.4.

Figure 9.4. An example of how the $.extend() function can merge nested objects

This function is really useful and you’ll use it a lot. Let’s now move on because we still have a few other utility functions to examine.

Serializing parameter values

It should come as no surprise that in a dynamic, highly interactive application. Submitting requests is a common occurrence. Frequently, these requests will be submitted as a result of a form submission, where the browser formats the request body containing the request parameters on your behalf. Other times, you’ll submit requests as URLs in the href attribute of a elements. In these latter cases, it becomes your responsibility to correctly create and format the query string that contains any request parameters you wish to include with the request.

Server-side templating tools generally have great mechanisms that help you construct valid URLs, but when creating them dynamically on the client, JavaScript doesn’t give you much in the way of support. Remember that not only do you need to correctly place all the ampersands (&) and equal signs (=) that format the query string parameters, but you also need to make sure that each name and value is properly URI-encoded. Although JavaScript provides a handy function for that (encodeURI-Component()), the formatting of the query string falls squarely into your lap.

As you might have come to expect, jQuery anticipates that burden and gives you a tool to make it easier: the $.param() utility function.

Function syntax: $.param
$.param(params[, traditional])
Serializes the passed information into a string suitable for use as the query string of a submitted request. The passed value can be an array of form elements, a jQuery object, or a JavaScript object. The query string is properly formatted and each name and value in the string is properly URI-encoded.
Parameters
params(Array|jQuery|Object) The value to be serialized into a query string. If an array of elements or a jQuery object is passed, the name/value pairs represented by the included form controls are added to the query string. If a JavaScript object is passed, the object’s properties form the parameter names and values.
traditional(Boolean) An optional flag indicating whether to perform a traditional shallow serialization. This generally affects only source objects with nested objects. If omitted, defaults to false.
Returns
The formatted query string.

To see this method in action, consider the following statement:

$.param({
   'a thing': 'it&s=value',
   'another thing': 'another value',
   'weird characters': '!@#$%^&*()_+='
});

Here you pass an object with three properties to the $.param() function, in which the names and the values all contain characters that must be encoded within the query string in order for it to be valid. The result of this function call is

a+thing=it%26s%3Dvalue&another+thing=another+value&weird+characters=!%40%23%2
     4%25%5E%26*()_%2B%3D

Note that the query string is formatted correctly and that the non-alphanumeric characters in the names and values have been properly encoded. This might not make the string all that readable to you, but server-side code lives for such strings!

One note of caution: if you pass an array of elements or a jQuery object that contains elements other than those representing form values, you’ll end up with a bunch of entries such as

undefined=&

in the resulting string, because this function doesn’t weed out inappropriate elements in its passed argument.

You might be thinking that this isn’t a big deal because, after all, if the values are form elements, they’re going to end up being submitted by the browser via the form, which will handle all of this for you. Well, hold onto your hat. In lesson 10, when we start talking about Ajax, you’ll see that form elements aren’t always submitted by their forms!

But that’s not going to be an issue, because you’ll also see later on that jQuery provides a higher-level means (that internally uses this very utility function) to handle this sort of thing in a more sophisticated fashion.

Let’s now consider another example. Imagine that you have the following form in your page:

<form>
   <label for="name">Name:</label>
   <input id="name" name="name" value="Aurelio" />
   <label for="surname">Surname:</label>
   <input id="surname" name="surname" value="De Rosa" />
   <label for="address">Address:</label>
   <input id="address" name="address" value="Fake street 1, London, UK" />
</form>

If you call the $.param() utility function by passing to it a jQuery object containing all the input elements of this form as shown here,

$.param($('input'));

you’ll obtain the following string as a result:

name=Aurelio&surname=De+Rosa&address=Fake+address+123%2C+London%2C+UK

This example should have clarified how $.param() works with form elements. But our discussion of this function isn’t complete yet.

Serializing nested parameters

Trained by years of dealing with the limitations of HTTP and HTML form controls, web developers are conditioned to think of serialized parameters, aka query strings, as a flat list of name/value pairs. For example, imagine a form in which you collect someone’s name and address. The query parameters for such a form might contain names such as firstnamelastname, and city. The serialized version of the query string might be this:

firstname=Yogi&lastname=Bear&streetaddress=123+Anywhere+Lane&city=Austin&state=TX&postalcode=78701

The preserialized version of this construct would be as follows:

{
  firstname: 'Yogi',
  lastname: 'Bear',
  streetaddress: '123 Anywhere Lane',
  city: 'Austin',
  state: 'TX',
  postalcode : '78701'
}

As an object, that doesn’t really represent the way that you’d think about such data. From a data organization point of view, you might think of this data as two major elements, a name and an address, each with its own properties, perhaps something along the lines of this:

{
  name: {
    first: 'Yogi',
    last: 'Bear'
  },
  address: {
    street: '123 Anywhere Lane',
    city: 'Austin',
    state: 'TX',
    postalcode : '78701'
  }
}

But this nested version of the element, though more logically structured than the flat version, doesn’t easily lend itself to conversion to a query string. Or does it?

By using a conventional notation employing square brackets, such a construct could be expressed as the following:

name[first]=Yogi&name[last]=Bear&address[street]=123+Anywhere+Lane&address[ci
     ty]=Austin&address[state]=TX&address[postalcode]=78701

In this notation, subproperties are expressed using square brackets to keep track of the structure. Many server-side languages such as PHP can handily decode these strings.

This is a smart behavior and it’s not the traditional way JavaScript treats such objects. What you might expect is something like

name=[object+Object]&address=[object+Object]

which isn’t helpful at all.

Fortunately, jQuery is able to deal with nested parameters, allowing you to decide when to apply the traditional or the smart behavior. All you need to do is pass true to obtain the traditional object and false (or omit it entirely) for the smart behavior, as the second parameter of $.param().

You can prove this to yourself with the $.param() Lab page provided in the file lesson-9/lab.$.param.html and shown in figure 9.5.

Figure 9.5. The $.param() Lab lets you see how flat and nested objects are serialized using the new and traditional algorithms.

This lab page lets you see how $.param() will serialize flat and nested objects, using its smart algorithm, as well as the traditional algorithm.

Go ahead and play around with this lab before moving to the next section. In the page we’ve set two sample objects so that you can immediately start testing $.param(). We also added the ability to edit them and to add new properties, so you can play with different object structures that you might want to serialize.

Testing objects

You may have noticed that many of the jQuery methods and utility functions have rather malleable parameter lists; optional parameters can be omitted without the need to include null values as placeholders. Take the on() method as an example. Its most used signature is

on(eventType[, selector][, data], handler)

If you have no selector or data to pass, you can simply call on() with the handler function as the second parameter; there’s no need for placeholders. jQuery handles this by testing the types of the arguments, and if it sees that there are only two arguments and that a function is passed as the second argument, it interprets that as the handler rather than as a selector or a data argument.

Testing arguments for various types, including whether they are functions or not, will certainly come in handy if you want to create your own functions and methods that are similarly friendly and versatile. For this reason, jQuery exposes a number of testing utility functions, as outlined in table 9.1.

Table 9.1. jQuery utility functions for testing objects
FunctionDescription
$.isArray(param)Returns true if param is a JavaScript array (but not if param is any other array-like object like a jQuery set); false otherwise
$.isEmptyObject(param)Returns true if param is a JavaScript object with no properties, including any inherited from prototype; false otherwise
$.isFunction(param)Returns true if param is a function; false otherwise
$.isNumeric(param)Returns true if param represents a numeric value; false otherwise
$.isPlainObject(param)Returns true if param is a JavaScript object created via {} or new Object(); false otherwise
$.isWindow(param)Returns true if param is the window object; false otherwise
$.isXMLDoc(param)Returns true if param is an XML document or a node within an XML document; false otherwise

Knowing these functions is nice, but you may feel that a practical example would be even nicer. Here you go!

Let’s say that you want a function that accepts either an array or an object as its first parameter and multiplies each numeric item of the array or value of the object by a given number passed as its second parameter. In addition, you want to specify a function that’s applied after the multiplication of an item as its third parameter. To have more fun, the second argument (which we’ll call factor) and the third one (which we’ll call customFunction) will be optional. This means that you can avoid both of them as well as just one. The function must return a new object of the same type as the first parameter, without modifying the latter.

Based on this description, the signature of the function can be represented like this:

multiplier(collection[, factor][, customFunction])

Thanks to the methods listed in table 9.1, you’re able to deal with all these cases without much hassle. Implementing the function results in code like that in the following listing. You can also find this code with some tests in the file lesson-9/testing.functions.html and as a JS Bin.

Listing 9.3. Testing utility functions

The code of this listing is interesting and uses many of the functions covered in this lesson, so we’ll describe it in more detail.

In the first part of the function, you define the calc() support function that has the core calculation . It deals with the variable parameters of the multiplier() function and it performs different operations based on the value passed as the second and the third arguments. If the second and the third arguments are undefined, you set factor to 1. Then the real calculation starts. If collection is of type Array, the function uses the $.map() utility function to call calc() on each array’s value, but only if it’s a number . If collection is of type Object, the function employs a for...in loop to call calc() on each object’s value, but only if it’s a number . Finally, the result is returned . The data type of the result depends on the data type of the first argument (Array or Object). In case collection is neither an Array nor an Objectnull is returned.

The functions described in this section allow you to test if a variable contains a value of a particular type (ObjectArrayFunction, and so on) or not. But what if the information you want to know is the type itself?

Discovering the type of a value

jQuery has one additional utility function that deals with types called $.type(). The syntax of this function is the following.

Function syntax: $.type
$.type(param)
Determines the type of a value
Parameters
param(Any) The value to test
Returns
A string describing the type of the value

To give you an idea of the result of calling this function, let’s say that you have a statement like the following:

$.type(3);

In this case you’ll obtain this result:

"number"

If you have this statement

$.type([1, 2, 3]);

you’ll obtain this string as the result:

"array"

This is an important difference compared to the usual way of testing types in JavaScript that will come in handy when we delve into developing plugins. In fact, if you perform the test

if (typeof [1, 2, 3] === 'array')

you’ll obtain false because the value returned by typeof [1, 2, 3] is "object".

Now that you have a complete overview of the utility functions that deal with data types, let’s look at a bunch of functions that allow you to parse strings of different types.

Parsing functions

jQuery provides a set of utility functions to parse several formats ranging from JSON to XML and HTML. Modern browsers provide an object called JSON that deals with the JSON format. This object has the parse() method that, as the name says, parses a JSON string. Exactly, modern browsers…. As always, this means that not all of your users have support for this feature. Fortunately, jQuery comes again to your help, providing the $.parseJSON() utility function.

Function syntax: $.parseJSON
$.parseJSON(json)
Parses the passed JSON string, returning its evaluation
Parameters
json(String) The JSON string to be parsed
Returns
The evaluation of the JSON string

You’ve seen several times that when the browser supports the native methods, jQuery uses it, and this case is no exception. If the browser supports JSON.parse(), jQuery will use it; otherwise it will use a JavaScript trick to perform the evaluation. In doing so, jQuery improves the performance of the operation where possible.

The JSON string must be completely well-formed, and the rules for well-formed JSON are much stricter than JavaScript expression notation. For example, all property names must be delimited by double-quote characters, even if they form valid identifiers. Invalid JSON will result in an error being thrown. See for the nitty-gritty on well-formed JSON.

JSON isn’t the only format used on the web to exchange information. Another commonly used one is XML. jQuery allows you to easily parse a string containing XML, turning it into its equivalent XML document by using the $.parseXML() utility function.

Function syntax: $.parseXML
$.parseXML(xml)
Parses an XML string into an XML document
Parameters
xml(String) The XML string to be parsed
Returns
The XML document derived from the string

XML documents are easy to use and traverse because jQuery supports them, so you can pass the object returned by the $.parseXML() function to jQuery and then use the methods you’ve learned so far. Sound crazy? Don’t worry; in a moment we’ll present a lab page that will enable you to play with this concept.

The last method belonging to this category is $.parseHTML(). It uses a native DOM element creation function to convert a string containing HTML markup to a set of DOM elements. Then, you can operate on these elements using the jQuery methods. The syntax of this function is the following.

Function syntax: $.parseHTML
$.parseHTML(html[, context][, keepScripts])
Parses a string into an array of DOM nodes.
Parameters
html(String) The HTML string to be parsed.
context(Element) An optional element to use as the context in which the HTML fragment will be generated. If not specified or if null or undefined is passed, the default value is the (current) document.
keepScripts(Boolean) If true the function will keep and include the scripts in the HTML string. The default value is false.
Returns
An array of DOM elements derived from the string.

In the description of the function, we specified that the default value of keepScripts is false. There’s an important security concern behind this decision. When fetching the HTML string from an external source, if you include the scripts, you enable a malicious person to perform an attack on your website.

It’s worth mentioning that in most environments, even if you get rid of the script elements, it’s still possible to perform an attack, so you should make certain you escape untrusted inputs from the source such as the URL or cookies. As an example of attacks that don’t come via a script element, think of those that can be performed using the onerror attribute of an img element.

The three utility functions we’ve just discussed gave us the chance to create another lab page, which you can find in the file lesson-9/lab.parsing.html and which is shown in figure 9.6.

Figure 9.6. The Parsing Functions Lab lets you play with the jQuery functions that deal with the three supported formats: JSON, XML, and HTML.

This lab page has three prebuilt snippets of code. The first one is a JSON object that you can query using dot notation. We made this possible by using the $.parse-JSON() function to turn the raw text into a JavaScript object.

Then another section allows you to query XML and HTML code in the same way you saw for the lab page of lesson 2. This time we’ll only show how many elements have been selected. For this part of the lab page we’ve employed $.parseXML() and $.parseHTML().

Note that you can modify all three code snippets because we put them into a textarea element. Therefore, you can test your own JSON object or any XML or HTML code that you want. Open the lab page and play with it.

Once you feel comfortable with these methods, you’re ready to learn about the other utility functions that can’t be grouped into a category.


by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *