Chapter 6. jQuery utility functions

This chapter covers

  • The jQuery browser detection flags
  • Using other libraries with jQuery
  • Functions for manipulating arrays
  • Extending and merging objects
  • Dynamically loading new script

Up to this point, we’ve spent a fair number of chapters examining jQuery commands—the term that we’ve applied to methods that operate upon a set of DOM elements wrapped by the $() function. But you may recall that way back in chapter 1, we also introduced the concept of utility functions—functions namespaced by $ that don’t operate on a wrapped set. These functions could be thought of as top-level functions except that they are defined on the $ instance rather than window.

Generally, these functions either operate upon JavaScript objects other than DOM elements (that’s the purview of the commands after all), or they perform some non-object-related operation.

You may wonder why we waited until this chapter to introduce these functions. Well, we had two primary reasons, which follow:

  • We wanted to guide you into thinking in terms of using jQuery commands rather than resorting to lower-level operations that might feel more familiar but not be as efficient or as easy to code as using the jQuery commands.
  • Because the commands take care of much of what we want to do when manipulating DOM elements on the pages, these lower-level functions are frequently most useful when writing the commands themselves (as well as other extensions) rather than in page-level code. (We’ll be tackling how to write our own plugins to jQuery in the next chapter.)

In this chapter we’re finally getting around to formally introducing most of the $-level utility functions, as well as a handful of useful flags. We’ll put off talking about the utility functions that deal with Ajax until the chapter that deals exclusively with jQuery’s Ajax functionality.

We’ll start out with those flags that we mentioned.

6.1. Using the jQuery flags

Some of the information jQuery makes available to us as page authors, and even plugin authors, is available, not via methods or functions but as variables defined on $. These flags are generally focused on helping us divine the capabilities of the current browser so that we can make decisions based on such information when necessary.

The jQuery flags intended for public use are as follows:

  • $.browser
  • $.boxModel
  • $.styleFloat

Let’s start by looking at how jQuery informs us which browser is being used.

6.1.1. Detecting the user agent

Thankfully, almost blissfully, the jQuery commands that we’ve introduced so far shield us from having to deal with browser differences, even in traditionally problematic areas like event handling. But when we’re the ones writing these commands (or other extensions), we often need to account for the differences in the ways browsers operate so that the users of our extensions don’t have to.

But before we dive into seeing how jQuery helps us in this regard, let’s talk about the concept of browser detection.

Why browser detection is heinous

OK, maybe the word heinous is too strong, but unless it’s absolutely necessary, browser detection is a technique that should only be used when no other options are available.

Browser detection might seem, at first, like a logical way to deal with browser differences. After all, it’s easy to say: “I know what the set of capabilities of browser X are, so testing for the browser makes perfect sense, right?” But browser detection is full of pitfalls and problems.

One of the major arguments against this technique is that the proliferation of browsers, as well as varying levels of support within versions of the same browser, makes this technique an unscalable approach to the problem.

You could be thinking, “Well, all I need to test for is Internet Explorer and Firefox.” But why would you exclude the growing number of Safari users? What about Opera? Moreover, there are some niche, but not insignificant, browsers that share capability profiles with the more popular browsers. Camino, for example, uses the same technology as Firefox behind its Mac-friendly UI. And OmniWeb uses the same rendering engine as Safari.

There’s no need to exclude support for these browsers, but it is a royal pain to have to test for them. And that’s not even considering differences between versions—IE6 and IE7, for example.

Another argument against browser detection (or sniffing as it’s sometimes called) is that it’s getting harder and harder to know who’s who.

Browsers identify themselves by setting a request header known as the user agent string. Parsing this string isn’t for the faint-hearted. In addition, many browsers now allow their users to spoof this string, so we can’t even believe what it tells us after we do parse it!

A JavaScript object named navigator gives us a partial glimpse into the user agent information, but even it has browser differences. We almost need to do browser detection in order to do browser detection!

Browser detection can be the following:

  • Imprecise— Accidentally blocking browsers that our code would work within
  • Unscalable— Leading to enormous nested if and if-else statements to sort things out
  • Inaccurate— Due to users spoofing false user agent information

Obviously, we’d like to avoid using it whenever possible.

But what can we do instead?

What’s the alternative?

If we think about it, we’re not really interested in which browser anyone is using, are we? The only reason we’re thinking about browser detection is so that we can know which browser capabilities we can use. It’s the capabilities of a browser that we’re after; using browser detection is just a ham-handed way of trying to determine those capabilities.

Why don’t we figure out what those capabilities are rather than trying to infer them from the browser identification? The technique known broadly as object detection allows code to branch based on whether certain objects, properties, or even methods exist.

Let’s think back to our chapter on event handling as an example. We remember that there are two advanced event-handling models: the W3C standard DOM Level 2 Event Model and the proprietary Internet Explorer Model. Both models define methods on the DOM elements that allow listeners to be established, but each uses different method names. The standard model defines the method addEventListener(), whereas the IE model defines attachEvent().

Using browser detection, assuming that we’ve gone through the pain and aggravation of (maybe even correctly) determining what browser is being used, we could write

...
complex code to set flags: isIE, isFirefox and isSafari
...
if (isIE) {
element.attachEvent('onclick',someHandler);
}
else if (isFirefox || isSafari) {
element.addEventListener('click',someHandler);
}
else {
throw new Error('event handling not supported'),
}

Aside from the fact that this example glosses over whatever necessarily complex code we are using to set the flags isIE, isFirefox, and isSafari, we can’t be sure if these flags accurately represent the browser being used. Moreover, this code will throw an error if used in Opera, Camino, OmniWeb, or a host of other lesserknown browsers that might perfectly support the standard model.

Consider the following variation of this code:

if (element.attachEvent) {
element.attachEvent('onclick',someHandler);
}
else if (element.addEventListener) {
element.addEventListener('click',someHandler);
}
else {
throw new Error('event handling not supported'),
}

This code doesn’t perform a lot of complex, and ultimately unreliable, browser detection; it automatically supports all browsers that support either of the two competing event models. Much better!

Object detection is vastly superior to browser detection. It’s more reliable, and it doesn’t accidentally block browsers that support the capability we are testing for simply because we don’t know about the capabilities of that browser.


Note

Even object detection is best avoided unless absolutely required. If we can come up with a cross-browser solution, it should be preferred over any type of branching.


But as superior to browser detection as object detection may be, it can’t always come to our rescue. There are times when we’ll need to make browser-specific decisions (we’ll see an example in a moment) that can only be made using browser detection.

So without any further ado, let’s get around to finally answering the question...

What are the blasted browser flags?

For those times when only browser detection will do, jQuery provides a set of flags that we can use for branching that are set up when the library is loaded, making them available even before any ready handlers have executed. They are defined as properties of an object instance with a reference of $.browser. The formal syntax for this flag set is as follows:


Flag syntax: $.browser

$.browser

Defines a set of flags that can be used to discover to which broad set of browser families the current user agent belongs. These flags are as follows:

  • msie Set to true if the user agent header identifies the browser as Microsoft Internet Explorer.
  • mozilla Set to true if the user agent header identifies the browser as any Mozilla-based browser. This includes browsers such as Firefox, Camino, and Netscape.
  • safari Set to true if the user agent header identifies the browser as Safari or any Safari-based browser such as OmniWeb.
  • opera Set to true if the user agent header identifies the browser as Opera.
  • version Set to the version number of the rendering engine for the browser.

Note that these flags don’t attempt to identify the specific browser that’s being used; jQuery classifies a user agent based upon which family of browsers it belongs to. Browsers within each family will sport the same sets of characteristics; specific browser identification should not be necessary.

The vast majority of commonly used, modern browsers will fall into one of these four browser families.

The version property deserves special notice because it’s not as handy as we might think. The value set into this property isn’t the version of the browser (as we might initially believe) but the version of the browser’s rendering engine. For example, when executed within Firefox 2.0.0.2, the reported version is 1.8.1.6—the version of the Gecko rendering engine. This value is handy for distinguishing between IE6 and IE7, containing 6.0 and 7.0 respectively.

We mentioned earlier that there are times when we can’t fall back on object detection and must resort to browser detection. One example of such a situation is when the difference between browsers isn’t that they present different object classes or different methods but that the parameters passed to a method are interpreted differently across the browser implementations. In such a case, there’s no object to perform detection on.

Let’s look at the add() method to the <select> elements. It’s defined as the following:

selectElement.add(element,before)

In this method, the first parameter identifies an <option> or <optgroup> element to add to the <select> element and the second identifies the existing <option> (or <optgroup>) that the new element is to be placed before. In standards-compliant browsers, this second parameter is a reference to the existing element; in Internet Explorer, it’s the ordinal index of the existing element.

Because there’s no way to perform object detection to determine if we should pass an object reference or an integer value, we must resort to browser detection as shown in the example page of listing 6.1, which can be found in the file chapter6/$.browser.html.

Listing 6.1. Testing for browsers

This example sets up a <select> element with four entries and a simple button. The button is instrumented to add a new <option> element between the second and third original options. Because Internet Explorer will expect the ordinal value 2, but W3C-standard browsers will expect a reference to the third existing option, we use browser detection to branch what gets passed as the second parameter to the add() method.

The before-and-after results for a sampling of browsers are shown in figure 6.1.

Figure 6.1. The option-adding functionality works flawlessly in a wide range of browsers.

The example code was executed in six modern browsers—Firefox, Safari, Opera, Camino, Internet Explorer 7, and OmniWeb—giving representation from each of the four browser families supported by the jQuery $.browser flag set. As can be seen, the addition of the option occurs correctly in each of the browsers.

Not to belabor the point, it’s important to once again note that this method of branching (browser detection) is far from optimal. The code that we wrote assumes that all non-Internet Explorer browsers will follow the W3C standard for the add() method to the <select> element. With testing, we’ve determined that this is true of the five non-Internet Explorer browsers depicted in figure 6.1 but what of other browsers? Would you know how Konqueror works without testing?

The bottom line is that because browser detection is such a broad brush, it requires thorough investigation of any browsers and platforms we wish to support with our code to know which branch a particular browser should take.

Leaving the subject of browser detection behind, let’s move on to another flag that helps us deal with browser differences.

6.1.2. Determining the box model

Which box model the current page is using is available via the Boolean $.boxModel flag, which is set to true if the page is using the W3C standard box model and false if the page is using the Internet Explorer box model (sometimes called the traditional box model).

“What’s the difference?” you may ask.

The box model determines how the size of the content of an element is determined in conjunction with its padding and border (margins, although part of the box model, take no part in determining the content size). Most browsers, other than Internet Explorer, support only the W3C box model, whereas Internet Explorer can use either model depending upon whether the page is being rendered in strict mode or quirks mode. Which mode is used depends upon the DOCTYPE (or lack thereof) declared for the page being rendered.

It’s beyond the scope of this book to go into a detailed examination of the various DOCTYPE issues, but some rules of thumb that work most of the time are

  • Pages with a valid and recognized DOCTYPE declaration are rendered in strict mode.
  • Pages lacking a DOCTYPE declaration or those containing an unrecognized declaration are rendered in quirks mode.

If you want to dig deeper into the DOCTYPE issues, an excellent resource to consult is http://www.quirksmode.org/css/quirksmode.html.

In a nutshell, the difference between the two box models centers on how the width and height styles are interpreted. In the W3C box model, these values determine the dimensions of the content of the element, not counting its padding and border widths; in the traditional box model, the values include padding and border width.

Let’s say that we have an element with the following styles applied:

{
width: 180px;
height: 72px;
padding: 10px;
border-width: 5px;
}

The different ways that the box models interpret the sizing of the element is diagrammed in figure 6.2.

Figure 6.2. The W3C box model excludes padding and border measurements from the width of an element, whereas the Internet Explorer model doesn’t.

Under the W3C box model, the size of the content of the element is 180 by 72 pixels exactly as specified by the width and height values. The padding and the border are applied outside this 180 by 72 pixel box, resulting in a total footprint of 210 by 102 pixels for the entire element.

When the traditional box model is used, the entire element is rendered in the 180 by 72 pixel box defined by the width and height attributes, reducing the size of the content to 150 by 42 pixels.

There are those on both sides of the fence that claim that one of these models is more intuitive or correct than the other, but the fact is we have to live with the difference.

If our code needs to account for these differences in that way that the elements will be rendered, the $.boxModel flag lets us know which is in force, and we can make our computations accordingly.

6.1.3. Detecting the correct float style to use

One other difference in browser capabilities that jQuery provides a flag for is the name used to represent the float CSS style in the element’s style property. This flag, $.styleFloat, evaluates to the string that should be used for the property name.

For example, to set the float value for an element, we use

element.style[$.styleFloat] = 'left';

This will account for any browser differences between the naming of this property—this flag evaluates to styleFloat for Internet Explorer and to cssFloat for other browsers.


Note

This flag is generally not required for on-page use. The css() wrapper method, when used with float, chooses the correct property (using this flag). The authors of plugins and other extensions are the target users of this flag for those cases where lower-level control is essential.


Now let’s leave the world of flags and look at the utility functions that jQuery provides.

6.2. Using other libraries with jQuery

Back in section 1.3.6, we introduced a means, thoughtfully provided for us by the jQuery team, to easily use jQuery on the same page as other libraries. Usually, the definition of the $ variable is the largest point of contention and conflict when using other libraries on the same page as jQuery. As we know, jQuery uses $ as an alias for the jQuery name, which is used for every feature that jQuery exhibits. But other libraries, most notably Prototype, use the $ name as well.

jQuery provides the $.noConflict() utility function to relinquish control of the $ name to whatever other library might wish to use it. The syntax of this function is as follows:


Function syntax: $.noConflict

$.noConflict()

Restores control of the $ name back to another library, allowing mixed library use on pages using jQuery.

Once this function is executed, jQuery features will need to be invoked using the jQuery name rather than the $ name.

Parameters

  • none

Returns

Undefined.


Because $ is an alias for jQuery, all of jQuery’s functionality is still available after the application of $.noConflict(), albeit by using the jQuery identifier. To compensate for the loss of the brief—yet beloved—$, we can define our own shorter, but non-conflicting, alias for jQuery, such as

var $j = jQuery;

Another idiom often employed is to create an environment where the $ name is scoped to refer to the jQuery object. This technique is commonly used when extending jQuery, particularly by plugin authors who can’t make any assumptions regarding whether page authors have called $.noConflict() and who, most certainly, can’t subvert the wishes of the page authors by calling it themselves.

This idiom is as follows:

(function($) { /* function body here */ })(jQuery);

If this notation makes your head spin, don’t worry! It’s pretty straightforward if odd-looking to those encountering it for the first time.

Let’s dissect the first part of this idiom:

(function($) { /* function body here */ })

This part declares a function and encloses it in parentheses to make an expression out of it, resulting in a reference to the anonymous function being returned as the value of the expression. The function expects a single parameter, which it names $; whatever is passed to this function can be referenced by the $ identifier within the body of the function. And because parameter declarations have precedence over any similarly named identifiers in the global scope, any value defined for $ outside of the function is superseded within the function by the passed argument.

The second part of the idiom

(jQuery)

performs a function call on the anonymous function passing the jQuery object as the argument.

As a result, the $ name refers to the jQuery object within the body of the function regardless of whether it’s already defined by Prototype or some other library outside of the function. Pretty nifty, isn’t it?

When employing this technique, the external declaration of $ isn’t available within the body of the function.

A variant of this idiom is also frequently used to form a third syntax for declaring a ready handler in addition to the means that we already examined in section 1.3.3. Consider the following:

jQuery(function($) {
alert("I'm ready!");
});

By passing a function as the parameter to the jQuery function, we declare it as a ready handler as we saw in section 1.3.3. But this time, we declare a single parameter to be passed to the ready handler using the $ identifier. Because jQuery always passes a reference to jQuery to a ready handler as its first and only parameter, this guarantees that the $ name refers to jQuery inside the ready handler regardless of the definition $ might have outside the body of the handler.

Let’s prove it to ourselves with a simple test. For the first part of the test, let’s examine the HTML document of listing 6.2.

Listing 6.2. Ready handler test 1

In this document, we import jQuery, which (as we know) defines the global names jQuery and its alias $. We then redefine the global $ name to a string value , overriding the jQuery definition. We replace $ with a simple string value for simplicity within this example, but it could be redefined by including another library such as Prototype.

We then define the ready handler whose only action is to display an alert showing the value of $.

When we load this page, we see the alert displayed, as shown in figure 6.3.

Figure 6.3. The $ says, “Hi!” as its redefinition takes effect within the ready handler.

Note that, within the ready handler, the global value of $ is in scope and has the expected value resulting from our string assignment. How disappointing if we only wanted to use the jQuery definition of $ within the handler.

Now let’s make one change to this example document. Listing 6.3 shows only the portion of the document that has been modified; the minimal change is highlighted in bold.

Listing 6.3. Ready handler test 2
<script type="text/javascript">
var $ = 'Hi!';
jQuery(function($){
alert('$ = '+ $);
});
</script>

The only change we made was to add a parameter to the ready handler function named $. When we load this changed version, we see something completely different as shown in figure 6.4.

Figure 6.4. The alert now displays the jQuery version of $ because its definition has been enforced within the function.

Well, that may not have been exactly what we might have predicted in advance. But a quick glance at the jQuery source code shows that, because we declare the first parameter of the ready handler to be $ within that function, the $ name refers to the jQuery function that jQuery passes as the sole parameter to all ready handlers (so the alert displays the definition of that function).

When writing reusable components, which might or might not be used in pages where $.noConflict() is used, it’s best to take such precautions regarding the definition of $.

A good number of the remaining jQuery utility functions are used to manipulate JavaScript objects. Let’s take a look at them.

6.3. 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 command. 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.

Let’s start with one that’s basic.

6.3.1. Trimming strings

Almost inexplicably, the JavaScript String implementation doesn’t possess a method to remove whitespace characters from the beginning and end of a string instance. Such basic functionality is customarily part of a String class in most other languages, but JavaScript mysteriously lacks this useful feature.

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

To help us out, jQuery defines the $.trim() function 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 by this function 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 characters u00A0, u2028, and u2029.

Parameters

value

(String) The string value to be trimmed. This original value isn’t modified.

Returns

The trimmed string.


A small example of using this function to trim the value of a text field in-place is

$('#someField').val($.trim($('#someField').val()));

Be aware that this function doesn’t check the parameter we pass to ensure that it’s a String value, so we’ll likely get undefined and unfortunate results (probably a JavaScript error) if we pass any other value type to this function.

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

6.3.2. Iterating through properties and collections

Oftentimes when we have non-scalar values composed of other components, we’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 us means to iterate over them. For arrays, we iterate over their elements using the for loop; for objects, we iterate over their properties using the for-in loop.

We can code examples of each as follows:

var anArray = ['one','two','three'];
for (var n = 0; n < anArray.length; n++) {
//do something here
}

var anObject = {one:1, two:2, three:3};
for (var p in anObject) {
//do something here
}

Pretty easy stuff, but some might think that the syntax is needlessly wordy and complex—a criticism frequently targeted at the for loop. We know that, for a wrapped set of DOM elements, jQuery defines the each() command, allowing us to easily iterate over the elements in the set without the need for messy for syntax. For general arrays and objects, jQuery provides an analogous utility function named $.each().

The same syntax is used whether iterating over the items in an array or the properties of an object.


Function syntax: $.each

$.each(container,callback)

Iterates over the items in the passed container, invoking the passed callback function for each.

Parameters

container

(Array|Object) An array whose items or an object whose properties are to be iterated over.

callback

(Function) A function invoked for each element in the container. If the container is an array, 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 item or property value.

The function context (this) of the invocation is also set to the value passed as the second parameter.

Returns

The container object.


This unified syntax can be used to iterate over either arrays or objects using the same format. Using this function, we write the previous example as follows:

var anArray = ['one','two','three'];
$.each(anArray,function(n,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 may seem like a six-of-one scenario in choosing syntax, 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:

$.each(anArray,someComplexFunction);

Note that when iterating over an array, we can break out of the loop by returning false from the iterator function. Doing so when iterating over object properties has no effect.

Sometimes we may iterate over arrays to pick and choose elements to become part of a new array. Let’s see how jQuery makes that easy.

6.3.3. Filtering arrays

Traversing an array to find elements that match certain criteria is a frequent need of applications that handle lots of data. We might wish to filter through the data looking 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 us to believe that the function employs the use of regular expressions like its namesake, the UNIX grep command. But the filtering criteria 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 if 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 the use of regular expressions is not 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 element. The return value of the callback function determines if 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|String) A function whose return value determines if the current data value is to be collected. 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.

This function is passed two parameters: the current data value and the index of that value within the original array.

A string can also be passed as this parameter that’s converted into the callback function. See the following discussion for details.

invert

(Boolean) If specified as true, it inverts the normal operation of the function.

Returns

The array of collected values.


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

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

The callback function that we pass to $.grep() can use whatever processing it likes to determine if the value should be included. The decision could be as easy as this example or, perhaps, even as complex as making synchronous Ajax calls (with the requisite performance hit) to the server to determine if the value should be included or excluded.

When the decision making is as simple as this example, jQuery provides a shortcut that we can use to make the statement more compact—provide the expression as a string. For example, we can rewrite our code snippet as

var bigNumbers = $.grep(originalArray,'a>100'),

When the callback is specified as a string, jQuery automatically generates a callback function using the passed string as the value of the return statement and passing two parameters: a for the current value and i for the current index. So the generated function for this example is equivalent to

function(a,i){return a>100;}

Even though the $.grep() function doesn’t directly use regular expressions (despite its name), JavaScript regular expressions can be powerful tools for us to use in our callback functions to determine whether to include or exclude values from the resultant array. Consider a situation in which we 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).

US 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 we could filter a source array for non-conformant 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 from arrays isn’t the only operation we might perform upon them. Let’s look at another array-targeted function that jQuery provides.

6.3.4. Translating arrays

Data might not always be in the format that we 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, jQuery makes it even easier with the $.map utility function.


Function syntax: $.map

$.map(array,callback)

Iterates through the passed array, invoking the callback function for each array item and collecting the return values of the function invocations in a new array.

Parameters

array

(Array) The array whose values are to be transformed to values in the new array.

callback

(Function|String) 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 data value and the index of that value within the original array.

A string can also be passed that’s converted into the callback function. See the following discussion for details.

Returns

The wrapped set.


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 an array of values, a zero-based set of indexes to a corresponding array of one-based indexes.

As with $.grep(), for such simple expressions, we can pass a string that represents the expression to make the statement more compact. The function generated on our behalf in such cases is passed the value as a parameter named a (unlike $.grep(), the index isn’t passed to auto-generated functions). We can rewrite our example as

var oneBased = $.map([0,1,2,3,4],'a+1'),

Another 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 one-to-one correspondence between items by order is lost.

Let’s look at a slightly more involved example. Imagine that we have an array of strings, perhaps collected from form fields, that are expected to represent numeric values and that we want to convert this array to an array of corresponding Number instances. Because there’s no guarantee against the presence of an invalid numeric string, we need to take some precautions. Consider the following code:

var strings = ['1','2','3','4','S','6'];

var values = $.map(strings,function(value){
var result = new Number(value);
return isNaN(result) ? null : result;
});

We 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 S instead of the expected 5. Our 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 Number.NaN. But the funny thing about Number.NaN is that by definition, it doesn’t equal anything else, including itself! Therefore the value of the expression Number.NaN==Number.NaN is false!

Because we 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 we employ to test the result of the string-to-numeric conversion.

In this example, we return null in the case of failure, ensuring that the resulting array contains only the valid numeric values with any error values elided. If we want to collect all the values, we can allow the transformation function to return Number.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','other thing'],
function(value){return value.split(''),}
);

This statement transforms an array of strings into an array of all of 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','o','t','h','e','r','','t','h','i','n','g']

This is accomplished by use of the String.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 there. There are a handful of minor functions that we might find handy.

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

Returns the index position of the first occurrence of the passed value

Parameters

value

(Object) The value for which the array will be searched

array

(Array) The array to be searched

Returns

The index of the first occurrence of the value within the array or -1 if the value is not 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.

“Other array-like objects? What on Earth is an array-like object?” you may ask.

jQuery considers other array-like objects to be any object that has a length and the concept of indexed entries. This capability is most useful for NodeList objects. Consider the following snippet:

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

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

Dealing with a NodeList is a bit of a pain, so converting it to a JavaScript array makes things a lot nicer. The jQuery $.makeArray function makes converting the NodeList easy.


Function syntax: $.makeArray

$.makeArray(object)

Converts the passed array-like object into a JavaScript array

Parameters

object

(Object) The array-like object (such as a NodeList) to be converted

Returns

The resulting JavaScript array


This function is intended for use in code that doesn’t make much use of jQuery, which internally handles this sort of thing on our behalves. This function also comes in handy when dealing with NodeList objects while traversing XML documents without jQuery.

Another seldom-used function that might come in handy 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

Parameters

array

(Array) The array of DOM elements to be examined

Returns

An array of DOM elements consisting of the unique elements in the passed array


Again, this is a function that jQuery uses internally to ensure that the lists of elements that we receive contain unique elements, and is intended for use on element arrays created outside the bounds of jQuery.

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

6.3.6. Extending objects

Although we all know that JavaScript provides some features that make it act in many ways like an object-oriented language, we know that 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—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.


Note

If you’re an aficionado of object-oriented JavaScript, you’ll no doubt be familiar with extending not only object instances but also their blueprints via the prototype property of object constructors. $.extend() can be used to effect such constructor-based inheritance by extending prototype, as well as object-based inheritance by extending existing object instances. Because understanding such advanced topics isn’t a requirement in order to use jQuery effectively, this is a subject—albeit an important one—that’s beyond the scope of this book.


It’s fairly easy to write JavaScript code to perform this extension by copy, but as with so many other procedures, jQuery anticipates this need and provides a ready-made utility function to help us out. As we’ll see in the next chapter, this function is useful for much more than extending an object, but even so its name is $.extend(). Its syntax is as follows:


Function syntax: $.extend

$.extend(target,source1,source2, ... sourceN)

Extends the object passed as target with the properties of the remaining passed objects.

Parameters

target

(Object) The object whose properties are augmented with the properties of the source objects. 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 name exist in the sources, sources later in the argument list take precedence over those earlier in the list.

Returns

The extended target object.


Let’s take a look at this function doing its thing. Examine the code of listing 6.4, which can also be found in the file chapter6/$.extend.html.

Listing 6.4. Putting the $.extend function to the test

In this simple example, we define three objects: a target and two source objects . We’ll use these objects to demonstrate what happens to the target when $.extend is used to merge the two sources into it.

After declaring the objects, we define a ready handler in which we’ll operate on them. Even though the objects are available immediately, we are going to display results on the page, so we need to wait until the HTML elements have been rendered to start playing around.

Within the ready handler, we display the state of the three objects in <span> elements defined to hold the results . (If you’re interested in how the $.toSource() function works, its definition can be found in the support.labs.js file. We’ll address adding such utility functions to our repertoire in the next chapter.)

We extend the target object with the two source objects using the following:

$.extend(target,source1,source2);

This merges the properties from objects source1 and source2 into the target object. The target object is returned as the value of the function; but, because the target is modified in place, we don’t need to create a variable to hold its reference. The fact that the target is returned is significant when using this function as part of a statement chain.

Then, we display the values for the modified target . The results are as shown in figure 6.5.

Figure 6.5. Using $.extend to merge object results in all source properties being copied into the target object.

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

  • Both the target and source1 contain a property named c. The value of c in source1 replaces the value in the original target.
  • Both source1 and source2 contain a property named e. Note that the value of e within source2 takes precedence over 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.

Although it’s evident that this utility function can be useful in many scenarios where one object must be extended with properties from another object (or set of objects), we’ll see a concrete and common use of this feature when learning how to define utility functions of our own in the next chapter.

But before we get to that, let’s wrap up our investigation of the utility functions with one that we can use to dynamically load new script into our pages.

6.4. Dynamically loading scripts

Most of the time—perhaps, almost always—we’ll load the external scripts our page needs from script files when the page loads via <script> tags in the <head> of the page. But every now and again, we might want to load some script after the fact under script control.

We might do this because we don’t know if the script will be needed until after some specific user activity has taken place but don’t want to include the script unless absolutely needed, or perhaps, we might need to use some information not available at load time to make a conditional choice between various scripts.

Regardless of why we might want to dynamically load new script into the page, jQuery provides the $.getScript() utility function to make it easy.


Function syntax: $.getScript

$.getScript(url,callback)

Fetches the script specified by the url parameter using a GET request to the specified server, optionally invoking a callback upon success.

Parameters

url

(String) The URL of the script file to fetch.

callback

(Function) An optional function invoked after the script resource has been loaded and evaluated.

The following parameters are passed:

  • The text loaded from the resource
  • The string success

Returns

The XHR instance used to fetch the script.


Under its covers, this function uses jQuery’s built-in Ajax mechanisms to fetch the script file. We’ll be covering these Ajax facilities in great detail in chapter 8, but we don’t need to know anything about Ajax to use this function.

After fetching, the script in the file is evaluated; any inline script is executed, and any defined variables or functions become available.


Warning

In Safari, the script definitions loaded from the fetched file don’t become available right away, even in the callback to the function. Any dynamically loaded script elements don’t became available until after the script block within which it is loaded relinquishes control back to the browser. If your pages are going to support Safari, plan accordingly!


Let’s see this in action. Consider the following script file (available in chapter6/new.stuff.js):

alert("I'm inline!");

var someVariable = 'Value of someVariable';

function someFunction(value) {
alert(value);
}

This trivial script file contains an inline statement (which issues an alert that leaves no doubt as to when the statement gets executed), a variable declaration, and a declaration for a function that issues an alert containing whatever value is passed to it when executed. Now let’s write a page to include this script file dynamically. The page is shown in listing 6.5 and can be found in the file chapter6/$.getScript.html.

Listing 6.5. Dynamically loading a script file and examining the results

This page defines two buttons that we use to trigger the activity of the example. The first button, labeled Load, causes the new.stuff.js file to be dynamically loaded through use of the $.getScript() function . Note that, initially, the second parameter (the callback) is commented out—we’ll get to that in a moment.

On clicking that button, the new.stuff.js file is loaded, and its content is evaluated. As expected, the inline statement within the file triggers an alert message as shown in figure 6.6.

Figure 6.6. The dynamic loading and evaluation of the script file results in the inline alert statement being executed.

Clicking the Inspect button executes its click handler , which executes the dynamically loaded someFunction() function passing the value of the dynamically loaded someVariable variable. If the alert appears as shown in figure 6.7, we know that both the variable and function are loaded correctly.

Figure 6.7. The appearance of the alert shows that the dynamic function is loaded correctly, and the correctly displayed value shows that the variable was dynamically loaded.

If you’d like to observe the behavior of Safari that we warned you about earlier, make a copy of the HTML file of listing 6.5, and uncomment the callback parameter to the $.getScript() function. This callback executes the click handler for the Inspect button, calling the dynamically loaded function with the loaded variable as its parameter.

In browsers other than Safari, the function and variable loaded dynamically from the script are available within the callback function. But when executed on Safari, nothing happens! We need to take heed of this divergence of functionality when using the $.getScript() function.

6.5. Summary

In this chapter we surveyed the features that jQuery provides outside of the methods that operate upon a wrapped set of matched DOM elements. These included an assortment of functions, as well as a set of flags, defined directly on the jQuery top-level name (as well as its $ alias).

When we need to resort to browser detection to account for differences in browser capabilites and operation, the $.browser set of flags lets us determine within which browser family the page is being displayed. Browser detection should be used only as a last resort when it’s impossible to write the code in a browser-independent fashion, and the preferred approach of object detection can’t be employed.

The $.boxModel flag tells us which of the two box models is being used to render the page, and the $.styleFloat flag lets us reference the style property of the float style in a browser-independent manner.

Recognizing that page authors may sometimes wish to use other libraries in conjunction with jQuery, jQuery provides $.noConflict(), which allows other libraries to use the $ alias. After calling this function, all jQuery operations must use the jQuery name rather than $.

$.trim() exists to fill the gap left by the native JavaScript String class for trimming whitespace from the beginning and end of string values.

jQuery also provides a set of functions that are useful for dealing with data sets in arrays. $.each() makes it easy to traverse through every item in an array; $.grep() allows us to create new arrays by filtering through the data of a source array using whatever filtering criteria we would like to use; and $.map() allows us to easily apply our own transformations to a source array to produce a corresponding new array with the transformed values.

To merge objects, perhaps even to mimic a sort of inheritance scheme, jQuery also provides the $.extend() function. This function allows us to unite the properties and any number of source objects into a target object.

And for those times when we want to load a script file dynamically, jQuery defines $.getScript(), which can load and evaluate a script file at any point in the execution of other page script.

With these additional tools safely tucked away in our toolbox, we’re ready to tackle how to add our own extensions to jQuery. Let’s get to it in the next chapter.

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

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