O'Reilly logo

JavaScript Cookbook by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 17. JavaScript Libraries

17.0. Introduction

JavaScript developers have never been luckier. Today, there are several very fine JavaScript framework libraries we can use for our applications that take care of many of the more tedious aspects of JavaScript development. In fact, you might be thinking about why you would even need a book on just plain JavaScript, when you can use framework libraries like Dojo, Ample SDK, Prototype, or jQuery (which I’m featuring later in this chapter). Why put up with the hassle of cross-browser issues, or the minutiae of creating and instantiating XMLHttpRequest object directly just to make a simple Ajax call?

I could reply that in order to use a framework library like jQuery to its best ability, you need to understand what’s happening under the hood. This is a valid answer, because if you don’t know what you can do with JavaScript, you won’t know what you can do with a JavaScript framework library.

However, I have another, more philosophical reason why you need to understand the basics of JavaScript, inspired in part by a short story by one of my favorite science fiction writers, Isaac Asimov.

Isaac Asimov wrote a story, “The Feeling of Power,” in the 1950s. In the story, a simple technician named Myron Aub astounds military, political, and scientific leaders with his astonishing discovery: the ability to perform mathematical calculations using only a person’s own mental ability, and a piece of paper and pencil.

In the future time in which Aub lives, people had become dependent on machines to do all of their calculations; they had forgotten how to perform even the most basic math. Asimov was inspired to write the story out of his growing concern about our increasing mechanistic dependencies. When you consider that high schools no longer teach basic mathematical procedures, such as how to compute a logarithm manually, because of our use of calculators and computers, Asimov demonstrated remarkable prescience with his story.

Now, I don’t think we’re in danger of forgetting how to code JavaScript from scratch, or how to build our own libraries—at least, not in the near future. But why let the framework library builders have all the fun with the language?

See Also

Directions for downloading jQuery are covered in a later section, but you can access Ample SDK at http://www.amplesdk.com/, find Prototype.js at http://www.prototypejs.org/, and check out Dojo Toolkit at http://www.dojotoolkit.org/. Another lightweight framework is MooTools.

17.1. Packaging Your Code

Problem

You want to package your code into one or more JavaScript files.

Solution

If your code is in one big file, look for opportunities to extract reusable functionality into self-contained objects in a separate library.

If you find you have a set of functions you repeat in all of your applications, consider packaging them for reuse via an object literal. Transform the following:

function getElem(identifier) {
   return document.getElementById(identifier);
}

function stripslashes (str) {
      return str.replace(/\\/g, '');
}

function removeAngleBrackets(str) {
      return str.replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

to:

var jscbObject = {

   // return element
   getElem : function (identifier) {
      return document.getElementById(identifier);
   },

   stripslashes : function(str) {
      return str.replace(/\\/g, '');
   },

   removeAngleBrackets: function(str) {
      return str.replace(/</g,'&lt;').replace(/>/g,'&gt;');
   }
};

Discussion

In the solution, I’ve taken three functions in the global space and converted them into three methods on one object. Not only does this reduce the clutter in the global space, but it helps prevent clashes with similar-sounding function names in other libraries.

Even as functions, though, they’re a step up from code that’s hardcoded to a specific use. For instance, if your code has the following to access a style property from an element:

// get width
var style;
var elem = div.getElementById("elem");
if (elem.currentStyle) {
   style = elem.currentStyle["width"];
} else if (document.defaultView &&
document.defaultView.getComputedStyle) {
   style = document.defaultView.getComputedStyle(elem,null).
getPropertyValue("width");
}

Repeating this code in more than one function in your application can quickly bloat the size of the JavaScript, as well as make it harder to read. Modularize the code by extracting it into a reusable function, and eventually into a new member of your library object literal:

var BBObjLibrary = {

   // get stylesheet style
   getStyle : function (obj, styleName) {
      if (obj.currentStyle)
         return obj.currentStyle[styleName];
      else if (document.defaultView &&
document.defaultView.getComputedStyle)
         return document.defaultView.getComputedStyle(obj,null).
getPropertyValue(styleName);
      return undefined;
   },
...
}

Even when you split your code into libraries of reusable objects, look for an opportunity to modularize your code into layers of functionality.

I have one library, bb.js, that provides basic functionality such as event handling, accessing generic style elements, processing keyboard events, and so on. I have another library, mtwimg.js, I use to provide image handling in a web page, similar to what the popular library Lightbox 2 provides. The latter is built up on top of the former, so that I don’t have to repeat the functionality in both libraries, but I also keep my bb.js library small, and focused.

When I created a third library, accordion.js, which creates automatic accordion widgets (also sometimes called collapsible sections), it was also built on the bb.js generic library, considerably reducing the development time. More importantly, if I eventually decide to drop support for my generic library in favor of another externally developed library, such as Dojo, Prototype, or jQuery, though the internal functionality in accordion.js and mtwimg.js has to change, the web pages that use both don’t, because the latter two libraries’ outward-facing functionality isn’t affected. This is a concept known as refactoring: improving the efficiency of your code without affecting the external functionality.

Oh, and while you’re at it: document your code. Though you may provide a minified version of your code for production use, consider providing a nonminified, well-documented version of your JavaScript libraries so that others can learn from your code, the same as you’re able to learn from theirs.

See Also

Recipe 17.3 covers how to minify your JavaScript. Object literals are covered in Recipe 16.11. You can download Lightbox 2 at http://www.huddletogether.com/projects/lightbox2/. Creating accordion sections is covered in Recipe 13.6.

17.2. Testing Your Code with JsUnit

Problem

You followed the good structuring practices outlined in Recipe 17.1 to create your JavaScript objects. Now you want to thoroughly test your code before packaging it into a library for distribution.

Solution

Use a tool like JsUnit to create test cases to formally test your library. If your library contains an object method like the following:

function addAndRound(value1,value2) {
    return Math.round(value1 + value2);
}

JsUnit test cases could look like the following:

function testAddAndRound() {
    assertEquals("checking valid", 6, addAndRound(3.55, 2.33));
    assertNaN("checking NaN",addAndRound("three",
"Four and a quarter"));
}

Both tests would be successful: the first because the result of the function is equal to the value 6, and both results are numbers; the second because the result of the function call is NaN, which is what the test checks.

Discussion

Chapter 10 covered error handling and debugging using a variety of tools, depending on your preferred development browser. Now, you’re ready to create more formal tests, not only for your own use, but for others. These types of tests are called unit tests.

A good rule of thumb when it comes to unit testing is that every requirement or use case for a library function (or object method) should have an associated test case (or cases). The unit test checks that the requirement is met, and the library function performs as expected. You can develop your own tests, but using something like JsUnit simplifies the test writing.

Note

The version of JsUnit I use is a JavaScript implementation of the well-known JUnit testing software, and was developed by Edward Hieatt. JsUnit can be downloaded at http://www.jsunit.net/, which also includes links for documentation and examples.

Once you’ve downloaded, unzipped, and installed the JsUnit folder, preferably in a test folder for your own application, create several test application pages to test your library’s functionality. Each page will contain a link to the JsUnit engine and your library. If the JsUnit library is located in its folder direction in your development subdirectory, link it as follows:

<script type="text/javascript" src="app/jsUnitCore.js"></script>
<script type="text/javascript" src="lib/myLibrary.js"></script>

Use the JsUnit methods in script in the page to perform the tests, and the JsUnit web page, testRunner.html, to actually run the test. How the test functions are written are based on JUnit testing methodology: the function names begin with test and can have no parameters.

The JsUnit assertions used to write tests are:

assert ([comment],booleanValue)

Tests that a function returns a Boolean value.

assertTrue([comment],booleanValue)

Tests for true return.

assertFalse([comment], booleanValue)

Tests for false return.

assertEquals([comment], value1, value2)

Tests return result or variable for equality.

assertsNotEquals ([comment],value1,value2)

Tests return result or variable against another value for nonequality.

assertNull([comment],value)

Tests for null value or return.

assertNotNull([comment],value)

Tests for nonnull value or return.

The first parameter in the functions—the comment—is optional. However, it does make it much easier to determine which test fails if you can see a unique comment for the test.

JsUnit supports other functions, such as setUp and tearDown, called before the tests are started and after they have finished, respectively. There are also three functions, which provide trace messages:

warn(message,[value])

Warning message, with optional value

inform(message,[value])

Information message, with optional value

debug(message,[value])

Debugging information, with optional value

Once you’ve created your test page, you run the tests within the testRunner.html page. If some of the tests fail, errors will display under the test progress bar. If you double-click the test, you can get the test fail information, as shown in Figure 17-1.

To see the JsUnit functionality in action, Example 17-1 contains a simple object with three methods.

Example 17-1. Simple JavaScript object with methods
var BBTest = {
  concatWithSpace : function(string1, string2) {
    return string1 + " " + string2;
  },
  discoverNumber : function(string, number) {
    return string.indexOf(number);
  },
  divideNumbers : function (value1, value2) {
    return value1 / value2;
  },
  addAndRound : function(value1, value2) {
    return Math.round(value1 + value2);
  }
}

Example 17-2 is a JsUnit test page that tests the object methods.

A JsUnit test run with some failed tests
Figure 17-1. A JsUnit test run with some failed tests
Example 17-2. JsUnit Test page to test methods for object in Example 17-1
<!DOCTYPE html>
<head>
<title>Testing Library</title>
<script src="jsunit/app/jsUnitCore.js"></script>
<script src="test.js"></script>
<script>

function testConcatWithSpace() {
  inform("Testing concatWithSpace");
  assertEquals("Checking equality", "Shelley
Powers",BBTest.concatWithSpace("Shelley","Powers"));
}

function testDiscoverNumber() {
  inform("Testing discoverNumber");
  assertEquals("Checking valid params",5,
BBTest.discoverNumber("found5value",5));
}

function testDivideNumbers() {
  inform("Testing divideNumbers");
  assertNaN("Checking numeric ops with no numbers",
BBTest.divideNumbers("five","2"));
  assertNotEquals("Checking not equals","5",
BBTest.divideNumbers(10,2));
}

function testAddAndRound() {
  inform("Testing addAndRound");
  assertNaN("Checking NaN",BBTest.addAndRound("four",
"Three Quarter"));
  assertEquals("Checking correct values",6,
BBTest.addAndRound(2.33,3.45));
}

</script>

</head>
<body>
<p>Running tests of test.js. View source to see tests.</p>
</body>

Load the test page into the testRunner.html page, located within the jsUnit subdirectory. All of the tests are successful, as shown in Figure 17-2.

Successful run of test page in
Figure 17-2. Successful run of test page in Example 17-2

JsUnit can be managed manually, or can be integrated into an automated testing environment, including integration with Apache Ant.

17.3. Minify Your Library

Problem

You want to compactly package your code for wider distribution.

Solution

After you’ve optimized your library code and tested it thoroughly through unit testing, compress it with a JavaScript optimizer.

Discussion

Once you’ve created your library, optimized it for efficiency, and run it through your unit testing, you’re ready to prep your code for production use.

One preparation to make is to compress the JavaScript as much as possible, so the file is small and loads quickly. JavaScript compression is handled through minify applications you can find online, such as the well-known JavaScript Compressor, created by Dean Edwards, shown in Figure 17-3.

Result of compressing the code from
Figure 17-3. Result of compressing the code from Example 17-1

See Also

You can find JavaScript minifiers and compressors all over the Web by searching for “compress JavaScript” or “JavaScript minify”. Dean Edwards’s JavaScript compression tool can be found at http://javascriptcompressor.com/.

17.4. Hosting Your Library

Problem

You want to open source your code, but you don’t want to have to maintain the libraries on your own server.

Solution

Use one of the source code hosts to host your code, and provide the tools to manage collaboration with other developers.

Discussion

One of the beautiful things about JavaScript is that many of the libraries and applications are open source, which means that not only are they typically free to use, but you can also adapt the library with your innovations, or even collaborate on the original. I strongly encourage open sourcing your libraries as much as possible. However, unless you have the resources to mount a public-facing source code control system, you’ll want to use one of the sites that provide support for open source applications.

One source code host is Google Code, which contains a simple user interface to start a new project and upload code. You can choose between two version control software systems (Subversion and Mercurial), as well as one of a host of open source licenses.

There is a wiki component to each project where you can provide documentation, and a way to provide updates for those interested in the project. The site also provides issue-tracking software for people to file bugs, in addition to a Downloads link and a separate link for source code.

The SVG-enabling software SVGWeb, mentioned in Chapter 15, is hosted in Google Code. Figure 17-4 shows the front page for the project and the links to all of the secondary support pages, including the Wiki, Downloads, Issues, Source, and so on. There is no charge for hosting an application on Google Code.

Another increasingly popular host for open source projects is github. Unlike Google Code, there are limits to what is supported for a free account on the service, but JavaScript libraries should not tax these limits. You shouldn’t be faced with costs, as long as your projects are open source and publicly available. However, if you want to use the service for a private collaboration with several others, this service is available at a cost.

SVGWeb hosted at Google Code
Figure 17-4. SVGWeb hosted at Google Code

As with Google Code, github supports source code control and collaboration from several people, including records of issues, downloads, a wiki support page, and a nice graphs page that provides graphics of language support, usage, and other interesting indicators.

The very popular jQuery library is hosted on github, as shown in Figure 17-5, though you download jQuery from its own domain.

The github page for the jQuery library
Figure 17-5. The github page for the jQuery library

Note

Source Forge used to be the place to host your open source software in the past. However, the site blocks access to certain countries listed in the United States Office of Foreign Assets Control sanction list, and has fallen out of favor with many open source developers.

17.5. Using an External Library: Building on the jQuery Framework

Problem

You want to create application-specific libraries without having to create your own library of reusable routines.

Solution

Use one of the framework JavaScript libraries, such as Prototype, Dojo, or jQuery, in order to provide the basic functionality you need, but isolate the use so that you can swap frameworks if needed.

Discussion

There are good reasons—aside from the time saved—for using an existing JavaScript framework library such as jQuery. One is that the code is more robustly tested by several people. Another is that you can tap into a community of support when you run into problems in your applications.

I’m focusing primarily on jQuery because it is the library incorporated into most of the applications I use, including Drupal, the Content Management System (CMS) I use at my site. It’s also small, specific, modular, and relatively uncomplicated. However, libraries such as Prototype, Dojo, Mootools, and others are also good, and you should examine each before making a decision.

Note

Download jQuery from http://jquery.com/. You can access both a minified version of the library and an uncompressed developer version.

To use jQuery, include a link to the library before providing links to your own or other, secondary libraries:

<script type="text/javascript" src="jquery.js"></script>

There are several application-specific libraries that are dependent on jQuery, so you may want to check if they provide jQuery as part of their own installation.

One aspect of jQuery that differs from most of the examples in this book is that jQuery’s “starting” point for script is not window.onload, as I’ve used with most of the code samples. Instead, the jQuery library provides a page start routine that waits for DOM elements to be loaded, but does not wait for images or other media to finish loading. This beginning point is called the ready event, and looks like the following:

$(document).ready(function() {
 ...
});

The code snippet demonstrates a couple of other things about jQuery. First, notice the dollar sign element reference: $(document). In jQuery, the dollar sign ($) is a reference to the main jQuery class, and acts as a selector for all element access in the application. If you’re working with jQuery, use the jQuery selector rather than code your own element access, because the jQuery selector comes with prepackaged functionality essential for jQuery to operate successfully.

The syntax you use when querying for page elements is the same as the syntax for the querySelector and querySelectorAll methods, described in Chapter 11. It’s based on CSS selector syntax for accessing a named element, such as the following:

#divOne{
  color: red;
}

Using jQuery to access this element looks like this:

$("#divOne").click(function() {
   alert("Well Hi World!");
});

This code snippet returns a reference to the div element identified by divOne, and then attaches a function to the element’s onclick event handler that prints out a message.

The code also demonstrates another fundamental aspect of jQuery—it makes heavy use of method chaining. Method chaining is a way of appending methods one to another. In the code, rather than return a reference to the div element and then attach the event handler function to it, you attach the event handler directly to the element request.

There is extensive documentation and tutorials on using jQuery, so I’ll leave an in-depth overview of jQuery for an off-book exercise. However, I did want to cover one important aspect of using jQuery—or any framework library with your own applications.

The key to making these work now and in the future is to wrap the library use in such a way that you can swap one library out for another, without having to recode your applications—or, at least, minimize the amount of recoding you would have to do.

Rather than use the jQuery ready event, create your own so you don’t build a higher-level dependency on jQuery. Rather than use jQuery methods directly in your business logic, use your own objects and methods, and call the jQuery methods within these. By providing a layer of abstraction between the implementation of your application’s business logic and the external framework library, if someday you stumble upon Frew, the Wonder Library, you can swap out jQuery (or Prototype, or Dojo) and build on Frew.

See Also

jQuery Cookbook by Cody Lindley (O’Reilly) is an excellent book providing a comprehensive overview and detailed how-tos for jQuery. It’s what I’m using to come up to speed on this powerful little library, and provides the best coverage on writing jQuery plug-ins (see Recipe 17.7).

See Recipe 11.4 for the use of the Selectors API and selector syntax. And see Recipe 16.13 for more on object method chaining.

17.6. Using Existing jQuery Plug-ins

Problem

You’ve made the decision to use jQuery as a framework. Now you want to incorporate some jQuery plug-ins and ensure they don’t clash with your object libraries.

Solution

When you’ve found the plug-in(s) you want to use, check the documentation for all methods and properties, not just those you’re interested in. Make sure that there isn’t anything in the plug-in that can generate unwanted side effects when used in conjunction with your own application. Focus on using plug-ins that provide exactly what you need, and only what you need. Try to avoid over-generic plug-ins. Also make sure there aren’t name clashes between the external plug-in and any you’ve created yourself.

As an example of a single-purpose, tightly focused plug-in, the jQuery Validation plug-in’s only purpose is to validate form field data. It can validate for Zip code, email address, even credit card format. To use, annotate your form fields with specific classes, such as required for required fields, or email to validate the field as email. An example is the following, from the jQuery plug-in site:

<form class="cmxform" id="commentForm" method="get" action="">
 <fieldset>
   <legend>A simple comment form with submit validation and default
messages</legend>
   <p>
     <label for="cname">Name</label>
     <em>*</em><input id="cname" name="name" size="25"
class="required" minlength="2" />
   </p>
   <p>
     <label for="cemail">E-Mail</label>
     <em>*</em><input id="cemail" name="email" size="25"
class="required email" />
   </p>
   <p>
     <label for="curl">URL</label>
     <em>  </em><input id="curl" name="url" size="25"  class="url"
value="" />
   </p>
   <p>
     <label for="ccomment">Your comment</label>
     <em>*</em><textarea id="ccomment" name="comment" cols="22"
class="required"></textarea>
   </p>
   <p>
     <input class="submit" type="submit" value="Submit"/>
   </p>
 </fieldset>
 </form>

Then, in your script, make one function call:

j$("#commentForm").validate();

It doesn’t get simpler than that.

Discussion

The plug-in in the solution, jQuery Validation, provides a small set of methods and several events that you can capture in order to perform any additional validation. You can also provide custom configuration for all displayed messages. Single-purpose, tightly focused plug-ins with few methods that provide events that you can intercept and customize for appearance should integrate well with your application.

Once integrated, though, you’ll also need to incorporate unit tests for the plug-in, in addition to your own functions. You’ll also have to check for updates to the plug-ins, and be aware of any issues and bugs. In particular, look for updates or potential problems with new releases of jQuery.

Note

The Validation plug-in can be downloaded at http://docs.jquery.com/Plugins/Validation. Look for jQuery plug-ins at http://plugins.jquery.com/.

17.7. Convert Your Library to a jQuery Plug-in

Problem

You want to convert your library methods and functions into a jQuery plug-in for use by others.

Solution

If you want your method to participate in the jQuery chain and be used with selectors, assign your method to the jQuery.fn property:

jQuery.fn.increaseWidth = function() {
   return this.each(function() {
      var width = $(this).width() + 10;
      $(this).width(width);
   });
};

If your plug-in has one or more separate functions that do not need to participate in the jQuery chain, or be attached to a selector, create a jQuery function directly on the jQuery object:

jQuery.bbHelloWorld = function(who) {
   alert ("Hello " + who + "!");
};

If your function uses the jQuery dollar sign function ($) and you’re concerned that the library could be used with other libraries that make use of $, wrap your function in an anonymous function. Instead of using the following jQuery method approach:

jQuery.fn.flashBlueRed = function() {
    return this.each(function() {
      var hex = rgb2hex($(this).css("background-color"));
      if (hex == "#0000ff") {
        $(this).css("background-color", "#ff0000");
      } else {
        $(this).css("background-color", "#0000ff");
      }
    });
 };

use the following anonymous function syntax:

;(function($) {
   $.fn.flashBlueRed = function() {
      return this.each(function() {
         var hex = rgb2hex($(this).css("background-color"));
         if (hex == "#0000ff") {
            $(this).css("background-color", "#ff0000");
         } else {
            $(this).css("background-color", "#0000ff");
         }
       });
    };
})(jQuery);

Discussion

It’s relatively simple to create a jQuery plug-in once you understand the nuances of the jQuery plug-in infrastructure.

If you’re interested in creating a jQuery method that can be used with a jQuery selector and participate in the jQuery chain, you’ll use the first syntax shown in the solution:

jQuery.fn.increaseWidth = function() {
   return this.each(function() {
      var width = $(this).width() + 10;
      $(this).width(width);
   });
};

However, if you want to make use of the dollar sign function ($) within the code, but still have the plug-in work within a multiple library setting, wrap the method in an anonymous function:

;(function($) {
   $.fn.flashBlueRed = function() {
      return this.each(function() {
         var hex = rgb2hex($(this).css("background-color"));
         if (hex == "#0000ff") {
            $(this).css("background-color", "#ff0000");
         } else {
            $(this).css("background-color", "#0000ff");
         }
       });
    };
})(jQuery);

Notice the following line in both examples:

return this.each(function () {

This code is necessary to allow the method to work on whatever is returned by the selector, regardless of whether it’s a single item or a group of items. The line begins the code block that includes your actual method code.

Check out the semi-colon (;) just before the anonymous function. I picked this trick up from Cody Lindley in jQuery Cookbook (O’Reilly). Putting the semicolon before the anonymous function ensures that the function won’t break if another plug-in forgets to terminate a method or function with a semi-colon.

If you’re only interested in adding a jQuery function that isn’t part of the jQuery chain or which makes use of a selector, use the jQuery function syntax:

jQuery.bbHelloWorld = function(who) {
   alert ("Hello " + who + "!");
};

Once you have created your plug-in code, package it in a separate file; to use the code, all someone has to do is include the script, following the jQuery script.

An example of a plug-in file is shown in Example 17-3. This file has a couple of functions, specific to converting an RGB value to a hexadecimal. All of the functions are added to the jQuery object, and each is preceded by “bb”, to act as namespace for the function.

The bbGetRGB function in the library is actually a function that exists as an internal (not publicly exposed) function within the jQuery User Interface (UI) library. It was originally created by Blair Mitchelmore for the highlightFade jQuery plug-in. However, I didn’t want to include the jQuery UI, so I just borrowed the function for the example.

Example 17-3. A jQuery plug-in
// Parse strings looking for color tuples [255,255,255]
// pulled from internal jQuery function
jQuery.bbGetRGB = function(color) {
    var result;

    // Check if we're already dealing with an array of colors
    if ( color && color.constructor == Array && color.length == 3 )
        return color;

    // Look for rgb(num,num,num)
    if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-
9]{1,3})\s*\)/.exec(color))
         return [parseInt(result[1],10), parseInt(result[2],10),
parseInt(result[3],10)];

    // Look for rgb(num%,num%,num%)
    if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.
[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
         return [parseFloat(result[1])*2.55, parseFloat(result[2])*
2.55, parseFloat(result[3])*2.55];

    // Look for #a0b1c2
    if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.
exec(color))
         return [parseInt(result[1],16), parseInt(result[2],16),
parseInt(result[3],16)];

    // Look for #fff
    if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.
exec(color))
        return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16),
parseInt(result[3]+result[3],16)];

    // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
    if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
        return colors['transparent'];

    // Otherwise, we're most likely dealing with a named color
        return colors[$.trim(color).toLowerCase()];
};

jQuery.bbPadHex = function (value) {
    if (value.toString().length == 1) {
         value = "0" + value;
     }
     return value;
};

jQuery.bbConvertRGBtoHex = function(rgbString) {
     var colors = $.bbGetRGB(rgbString);
     var red = $.bbPadHex(parseInt(colors[0]).toString(16));
     var green = $.bbPadHex(parseInt(colors[1]).toString(16));
     var blue = $.bbPadHex(parseInt(colors[2]).toString(16));

     return "#" + red + green + blue;
};

bbPadHex, bbConvertRGBtoHex, and bbGetRGB are added as jQuery functions, the flashBlueRed method is wrapped in an anonymous function, and the increaseWidth method is a straight jQuery method. Example 17-4 shows the two methods in action. Notice how the increaseWidth method is chained to the flashBlueRed method, and both work on the element returned with the jQuery selector.

Example 17-4. Web page and application that use the new plug-in
<!doctype html>
<html>
  <head>
  <style>
  #test
  {
    background-color: #0000ff;
    width: 500px;
    padding: 10px;
    color: #ffffff;
    font-weight: bold;
    font-size: larger;
  }
  </style>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="basic.js"></script>

    <script type="text/javascript">

       $(document).ready(function() {
         $("#test").click(function() {
             $(this).flashBlueRed().increaseWidth();
         });
       });
    </script>
  </head>
  <body>
  <div id="test">
    hi, click me to change color
  </div>
  </body>
</html>

17.8. Safely Combining Several Libraries in Your Applications

Problem

You want to incorporate more than one external library, as well as your own, into one application without each stepping all over the others.

Solution

The safest approach for using multiple libraries is to pick ones that are all based on the same framework, such as using only libraries based on Dojo, Prototype, or jQuery, the framework used in earlier recipes.

If that strategy doesn’t work, make sure the libraries all use good programming practices, and none are overriding functionality or event handling provided by the others.

Discussion

Regardless of library purpose, there are fundamental rules governing the behavior of libraries that must be followed. Well-designed libraries do not do things like this:

window.onload=function() {...}

I use the DOM Level 0 window.onload event handler with the examples in the book because it’s quick, simple, and doesn’t add a lot of code to the sample. However, if you have one library that uses the old DOM Level 0 event handling, it will overwrite the event capturing utilized by the other libraries and your own application. Well-designed libraries don’t use DOM Level 0 event handling. Well-designed libraries also namespace all of their functionality. You won’t find the following in a well-defined library:

function foo() { ... }
function bar() { ... }

Each function like this ends up in the global space, which increases the likelihood of clashes with other libraries, and your own applications. Well-designed libraries use object literals to namespace their functionality:

var BigObject = {
   foo : function () { },
   bar : function () { }
}

A library that plays well with other libraries and applications will not extend existing objects via the prototype object. Yes, I know it’s a wonderful way of extending objects, and fundamental to JavaScript, but you can’t control one library from overriding another if both are extending the prototype property for the same object. Besides, if the framework and external libraries you use don’t extend existing objects via the object’s prototype, this leaves you free to play in your application.

Come to that, library builders should never assume that their library is the only one used in a project.

Well-designed libraries provide event hooks so that you can hook into the library at the points where it performs a major action. In Recipe 17.7, the jQuery plug-in described in the solution provided event handler hooks you can use to provide your own functionality before or after the plug-in’s validation routine.

Well-designed libraries provide good documentation of all of the publicly exposed bits, including methods, properties, and events. You shouldn’t have to guess how to use the library, or examine minute portions of the code, in order to figure out what you need to do.

Well-designed libraries are well-tested, and provide a way to report bugs and view existing bugs. If there’s a major security problem with an existing library, you need to know about it. If there are minor bugs, you need to know about these, too. Libraries that provide a subdirectory with self-tests rate high in my book.

Well-designed libraries provide nonminified, original source code. This isn’t essential—just helpful, and something I look for in a library.

It goes without saying that a good library is one actively maintained, but it can’t hurt to repeat this assertion. An even better library is one that’s open sourced, and maintained by a community of users—or is one you can maintain on your own, if the original maintainer can no longer perform this action.

To summarize:

  • A good library does not use DOM Level 0 event handling.

  • A well-defined library uses object literals to namespace its functionality.

  • A well-defined library introduces few global objects.

  • Libraries that play well with others provide event hooks. Well-behaved libraries also don’t extend existing objects via the prototype property.

  • Solid libraries are well-tested, and hopefully provide these self-tests as deliverables.

  • Stable libraries are actively maintained and, preferably, open sourced.

  • Secure libraries provide documentation of known bugs and problems, and a way to report on any bugs and problems you find.

  • Usable libraries are well-documented. Bandwidth-friendly libraries are optimized and compressed, though you can always compress the library yourself

  • Confident libraries aren’t built on the assumption that no other library will be used.

For the most part, you should be able to find what you need and have it work with your preferred framework. Be cautious when it comes to using a library that requires you add a new framework, which then needs to coexist with another framework. However, most well-built framework libraries could work with others.

As an example of framework coexistence, and since I’m focusing on jQuery in this chapter, if you use jQuery, you can use another framework library, such as Prototype, MooTools, or Dojo. The use of global namespaces should prevent name clashes. The only exception to the namespace rule is the dollar sign ($), function, which is also used by Prototype. You can override the use of the $ by adding the following, after all the libraries have been loaded:

jQuery.noConflict();

Once you add this code, instead of:

$("#elem").fadeOut('slow');

use:

jQuery("#elem").fadeOut('slow');

There are other approaches, too, including assigning a new short character replacement, but these are detailed in the jQuery documentation.

You can use most well-made framework libraries together, but there is tremendous overlap in functionality between the libraries, and this overlap in functionality comes with a cost: bandwidth to download the libraries. Try to avoid using more than one framework library at a time. Find the one you like, and be prepared to commit to it for some time to come.

See Also

The jQuery web page documenting how to use the framework with other libraries is at http://docs.jquery.com/Using_jQuery_with_Other_Libraries.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required