| JavaScript On The Web |
|
|
|
|
|
|
|
|
| This document takes 20 to 35 minutes to read |
|
|
|
Now that we’ve covered the basics of JavaScript and the DOM, let’s work with our new tools. In this chapter we have picked seven projects that will help you build your own projects, as well as illustrate many of the techniques and JavaScript features we have covered in other chapters:
Working with JavaScript
Loading Scripts Efficiently
Asynchronous Communication using XMLHttpRequest
Cross-Domain Techniques
Data Caching
Choosing a JavaScript Library
Using jQuery
Building Your Own JavaScript Library
Working with JavaScript
Although not technically a “project,” we wanted to discuss some important aspects of working with JavaScript. Probably the most common questions we get from JavaScript novices have to do with working with JavaScript: which editors are good? How do you debug? What’s the best environment to work with? Are there any tricks to working with the language? We wanted to take this opportunity to answer these questions.
Over the years, we’ve written JavaScript in just about every environment imaginable. One of the great things about JavaScript is that you don’t need a lot of tools to work with it. A simple text editor and a browser will suffice to get you started, and for basic projects that’s really all you need. Once you start working on projects with a little complexity, though, you’ll quickly find yourself wanting more advanced tools.
In this section we want to cover the basic tools of the JavaScript trade. To start we wanted to go over the trinity of JavaScript development tools: integrated development environments, browsers, and personal web servers.
We’ll start by talking a little about some of the more popular integrated development environments (IDEs) with JavaScript support. Having a solid IDE with features like syntax highlighting, code completion, refactoring support, and collaboration capabilities can help tame a complex project. There are many available, and it’s hard to know which one to pick.
We also want to cover the developer support provided by web browsers. Modern web browsers provide a wide variety of very useful tools for monitoring and debugging the JavaScript that they’re running.
Finally we’ll cover the most commonly-used personal web servers. You can use your browser’s Open File feature to test your scripts, and that’s okay for basic work. Asynchronous communication, however, is one of the cornerstones of building JavaScript applications, and it requires a web server. (We cover asynchronous communication in the Asynchronous Communication with XMLHttpRequest section, below.)
Once we’ve covered the tools of the trade we will talk about how to use them. We’ll provide some insights into our usual workflow when working with JavaScript, and then talk a little about methods for debugging your scripts.
JavaScript IDEs
Since JavaScript is essentially text, all you really need to write it is a text editor. Any text editor can serve the purpose, even something simple like the Notepad application on Windows. There are also several code editors available that work well with JavaScript, and some even provide basic features like syntax highlighting. We’ve used vi to create projects, and know several colleagues who are die-hard emacs users. We’re also very fond of TextMate and Sublime Edit, two great editing programs that support a variety of languages.
When you start working with complex projects with many JavaScript files, you’ll quickly find that you’ll need more features than simple code editors can provide. That’s where integrated development environments (IDEs) come into play. (And if you’re already familiar with code editing environments for other languages, you’ll want the same features for your JavaScript projects.)
An integrated development environment takes a code editor to the next level. A typical IDE will provide features for managing multiple files and file types, grouping them together in projects or applications (the exact term varies), and will often provide features for collaborating with other developers, such as integration with source control systems.
There are several IDEs that support JavaScript development. Typically they all provide a basic set of editing features (e.g. file creation, deletion, renaming, moving; find/replace in a single file or across multiple files; auto indenting). The better IDEs will provide more advanced features like code assist (a feature that acts as a dynamic assistant, providing suggestions or autocompletion based on what you have typed).
In addition, much of the time you won’t be working exclusively with JavaScript. If you’re working on a typical web project you’ll also be working with HTML and CSS, so you’ll want the IDE to support those as well. We find, however, that we can live with fewer features for HTML and CSS in an IDE if the JavaScript support is particularly good.
If you’re new to using IDEs entirely, we recommend looking through this list and trying a couple of the options. Many developers are passionate about their IDEs and will claim that their choice is the only logical one, but really the choice is one of personal preference. Trying a few IDEs will help you figure out what your preferences are—which features you really like, which ones you can work without—and you can make a choice at that point.
aptana studio
http://www.aptana.com
Aptana Studio is our preferred IDE. It’s built on Eclipse (see below) and is available as either a standalone install or as an Eclipse plugin. Aptana has all of the features of the base Eclipse IDE (build integration, cross-platform, scriptability, etc). In addition, Aptana has several very useful features for web development in general, and JavaScript development in particular:
Code assist with JavaScript, HTML, and CSS.
Code assist with jQuery (which has its own syntax, see Using jQuery, below).
Out-of-the-box Git integration (Git is a highly popular source control system; see http://git-scm.com/ for details).
Built-in CLI, for working with script interpreters like node (or ruby if you’re working with Rails).
JSLint integration (JSLint is a JavaScript syntax and style checker; see http://www.jslint.com/lint.html for details).
Open Source, with an active community. Bug reports are answered quickly and fixes are pushed regularly via the internal updating system.
Because it is built on Eclipse, many existing Eclipse plugins will work with Aptana (e.g. the SVN plugin).
Aptana is free.
Aptana also has the fully configurable UI of Eclipse, and also provides a nice library of predefined themes to try and change to your liking. (We’re very fond of the “Espresso Libre” theme.)
Aptana is backed by Appcelerator, a company that focuses on creating tools for building mobile applications. See http://www.appcelerator.com for details.
Eclipse
http://www.eclipse.org
Eclipse is an open-source IDE. Eclipse was built for Java development (and is itself built using Java), but now includes support for many other languages: JavaScript, C/C++, Ruby, Python, PHP, etc. Eclipse is a full-fledged IDE, so it can handle entire projects, and even has integration with popular build systems like Ant. In addition:
Eclipse is cross-platform. Because it’s built with Java, Eclipse runs on many operating systems. This means you can learn Eclipse once and not have to worry about changing from Windows to Mac and having to learn a whole new IDE.
Eclipse supports plugins. Anyone can create plugins to extend Eclipse’s functionality, and many plugins exist for everything from browser debugging to unit testing to SVN and Git integration.
Eclipse is Open Source and has an active community.
Eclipse is free.
Out of the box Eclipse provides very little support for working with JavaScript (or HTML or CSS). You can add varying degrees of support through a plugin. The most prominent Eclipse plugin for JavaScript support is Aptana (see above), but if that’s too much there are a couple other choices.
We’ve had good luck with Amateras http://amateras.sourceforge.jp/cgi-bin/fswiki_en/wiki.cgi?page=EclipseHTMLEditor which provides basic functionality including code highlighting, content assist, outlining, and validation. If all you’re looking for is basic coding support in a lightweight plugin, Amateras is a good choice.
The Eclipse Web Tools Platform is a project that aims to provide tools for all aspects of web development. You can read more at http://www.eclipse.org/webtools/. They have a JavaScript Developer’s Tools (JSDT) sub-project at http://www.eclipse.org/webtools/jsdt/ that works very well, and provides basic code editing features as well as integrated debugging, code assist with browser dependencies, code outlining and perspectives, etc. When we are working with a plain Eclipse install and cannot use Aptana, the Eclipse Web Tools Platform is our preferred plugin for JavaScript support.
Microsoft Visual Web Developer and Visual Studio Express
http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products
Microsoft’s Visual Studio is an excellent IDE, but quite expensive for a full license. To make their tools (and thus their platforms) easier to access, Microsoft has created a line of “express” versions of Visual Studio. The Visual Studio Express for Web IDE is a great environment for creating HTML/CSS/JavaScript applications. We’ve worked with the 2010 version (which was called Visual Web Developer) and it provided all the features we needed for integrating web-based UI work with .NET backend work. Features:
Integrated workflows with full versions of Visual Studio, allowing you to keep the expensive licenses to a minimum without limiting collaboration.
Code highlighting and syntax checking for JavaScript and HTML.
SVN and Git integration.
Unit testing integration.
Active community of users who are helpful about answering questions and providing suggestions.
Free.
If you will be building projects for Windows, or will be collaborating with people who do, the Visual Studio line would be a great choice.
WebStorm
http://www.jetbrains.com/webstorm
We know many JavaScript developers that swear by WebStorm and will not even consider using another IDE. Its features include:
Code highlighting and completion.
Unit testing integration.
JSlint integration.
Internal debugger.
30-day free trial, $49 for a personal license.
We’ve not used WebStorm ourselves, but we have seen enough of it over the shoulders of colleagues to be suitably impressed.
Browsers
Probably as important as your choice of IDE is your choice of browser for doing your development. You’ll be testing in all of your target browsers, of course, but you’ll have one main browser that will be your go-to choice for ongoing work, that will always be running in the background waiting for you to switch back to it and hit refresh to see your latest changes. Which browser you choose as this faithful companion will depend largely on how well it supports JavaScript development.
Not long ago, browsers had very little support for developers. You could view the source of a web page but that was it. Errors in your JavaScript (or HTML or CSS) would pass unremarked, except for inducing unpredictable behavior in your applications. Debugging a complex script was as much a matter of defensive coding and knowledge of arcane idiosyncrasies of the browser as it was a matter of following any sort of set pattern.
Today all the modern browsers (Safari, Chrome, Firefox, and Internet Explorer) have tools for supporting development. Some browsers provide highly advanced features, but all of them provide at least a JavaScript console and the ability to view generated source.
JavaScript console: They all provide a console where the browser can output error messages. The browsers also provide access to the console to your scripts (see Using the Console, below).
View generated source: All modern browsers provide a way for you to view the source of a document that results after all of the JavaScript within the page has run. If that JavaScript has modified the DOM, those changes will be reflected in the markup. This feature is enormously helpful in determining if your DOM manipulations are behaving as you expected. Each browser’s implementation of this feature is different; consult with your browser’s documentation to learn how to use it.
Most browsers have other features that are quite useful, including the ability to monitor HTTP traffic as it happens (which is helpful when you’re making asynchronous requests and are wondering if the server you queried returned the response you expected), the ability to step through code line by line, the ability to directly manipulate HTML, CSS, and even JavaScript as the application is running, etc.
In addition to native development support, some browsers (most notably Firefox) have an extensive library of third party add-ons that provide even more features. In fact, third party add-ons were among the first developer tools available. Their popularity helped convince browser manufacturers that providing native tools is a great way to attract developers to using their browsers.
Developer support features evolve constantly because modern browsers are also evolving and push regular and frequent updates. For example, Firefox recently added a 3-D view to their developer tools, which provides a three-dimensional view of the DOM which you can view from different angles.
Chrome
Google Chrome is our go-to browser for web development. The developer tools are robust and feature the ability to apply break points to JavaScript code, run stack traces, profile efficiency, and more. We work with Chrome on a daily basis and have not yet found its developer tools to be wanting.
Firefox
Firefox is probably the most extensible of the web browsers. The latest versions include an impressive set of development tools which includes the highly useful Scratchpad, which enables you to enter JavaScript code and run it in the context of the current tab.
Firefox didn’t always have such robust development tools built in, however, so for many years web developers had to rely on plugins to provide that functionality. Probably the most popular is Firebug (http://www.getfirebug.com) which provides all the features you need for web development in Firefox: a DOM inspector, script and network monitors, a JavaScript console, etc.
Another favorite Firefox extension is the Web Developer’s Toolbar (https://addons.mozilla.org/en-US/firefox/addon/web-developer/?redirectlocale=en-US&redirectslug= Web_Developer_Extension_%28external%29). This extension adds a toolbar to Firefox with many useful features: The ability to selectively enable or disable features such as scripts, image display, or Firefox’s native popup blocker; inspectors for the DOM, cookies, forms; validation tools, etc.
Internet Explorer
Internet Explorer’s development tools are a relatively new addition, but as of version 10 are quite extensive. To access Internet Explorer’s developer’s tools, hit F12 while viewing a page. This will bring up a window containing the available tools.
Internet Explorer’s tools include all of the basic ones: a console, the ability to add breakpoints to your JavaScript, efficiency profiling, network monitoring, etc. In addition, it provides several useful features such as standards validation, which submits the current page to various validators for checking. IE’s developer tools also includes an accessibility validator, which submits the current page to the validator at www.contentquality.com.
IE’s dev tools also help you manage some of Internet Explorer’s more quirky aspects. For example, using conditional comments you can write code that targets specific versions of Internet Explorer, the tools provide a way to see what mode you currently are using. You can also change modes, making it easier to test your work in different viewing modes.
(Explaining document modes in IE is a bit beyond the scope of this section; for a really good explanation see http://www.nczonline.net/blog/2010/01/19/internet-explorer-8-document-and-browser-modes/.)
Safari
Safari also has an extensive set of web developer tools. To use them go to Preferences ➤ Advanced and check the “Show Develop menu in menu bar” option. That will add a Develop item to the main menu bar, which is how you access Safari’s developer tools.
Safari’s developer tools are quite similar to Chrome’s, and that makes sense because they both share a common codebase. However, the developers tools do differ between the two browsers. Safari, for example, has the surprisingly useful Snippets editor: From the Develop menu choose “Show Snippet editor” to pop open the editor. The Snippets editor essentially provides a stripped-down browser for you to work with. Type in your HTML, CSS, and JavaScript in the top pane and it will instantly be rendered in the bottom pane. The Snippets editor is great for testing styles and prototyping interactions, giving you a place to quickly try code without having to go through the extra steps of creating a full HTML file.
Web Servers
Now that you have an editor for creating JavaScript and a browser to run it in, you need a way to get your scripts into the browser. Many of the examples in this book can be saved as simple files on your hard drive and then opened directly in the browser, but for real web development you’ll want to run your own local server for testing your work. Each operating system has its own options for web servers, and there is even one cross-platform option available.
MacOS
MacOS comes with an Apache web server built in, and prior to Mountain Lion you could activate it via the Sharing pane in the Control Panel. Apple removed the Control Panel interface, but left the server intact. You can manage the server from the command line, or we were able to find at least one person who had posted a custom Control Panel pane that purportedly restored the feature. (We didn’t try that, but if you want to give it a try head to your favorite search engine and search for “replacement system preferences pane web sharing” and you should find it right away.)
Windows
Windows has its own web server called Internet Information Services, or IIS. Most of the various editions of Windows 7 and Windows 8 do not come with IIS installed or enabled by default, but you can easily add it to your installation. We can’t cover the details here (that would be an entire chapter in and of itself), but searching for “windows 7 IIS install” or “windows 8 IIS install” should get you started.
Xampp
http://www.apachefriends.org/en/xampp.html
Xampp is an easy to install cross-platform version of the Apache web server (configured with PHP), along with the MySQL database and several other useful tools. It’s available for Windows, MacOS, Linux, and Solaris. We’ve had very good luck with Xampp on both Windows and MacOS, and recommend it.
IDE Debugging Servers
Many IDEs have built-in debugging servers. These will allow you to serve a single page, or possibly an entire project, from within the IDE. In addition to simply serving the files, the IDE will also often provide integrated features for debugging your code, like breakpoints, stack traces, and stepping through code, all in the same environment. We’ve found that the Visual Studio tools for integrated debugging are particularly good, but Aptana’s tools are also quite useful. Consult the documentation for your tool for details on how to configure and use your IDE’s integrated server.
JavaScript Development Workflow
Now that you’ve picked an editor, a browser, and a way to serve your files, you’re ready to go. At its most simple, a typical JavaScript development workflow looks like the workflow for any other language (write, test, repeat):
1. Write some code.
2. Load it in the browser and observe the results.
3. Repeat, fixing the problems that occurred or adding more features.
As you write more complex JavaScript, you’ll need ways to inspect your scripts as they execute. Some IDEs and browser developer tools provide features that help, like inspectors or breakpoints, but there are some basic techniques you can use that give you a lot of what you need.
Using the Browser Console
Throughout this book we’ve been using alerts to monitor the progress of our scripts. Alerts are fine, but they have the disadvantage of pausing the execution of a script while it waits for someone to click the OK button.
Fortunately, there is a robust alternative to alerts in the browser’s JavaScript console. All modern browsers have a JavaScript console that you can access, typically as part of the browser’s built-in Developer Tools, see Figure 1 for an example. Most browsers have a keyboard shortcut to bring up the console: for Chrome it’s Shift-Control-I, for Firefox it’s Shift-Control-J. IE also has a console; hit F12 to bring up the Developer Tools and click on the Console tab.
Figure 1. The Screenshot of the Chrome browser console
Modern browsers expose an interface for the console to JavaScript in the form of the window.console object. Each browser’s console is different, so each browser’s console object is different. But all of them provide methods for outputting your own text to the console:
console.log(strText): Output the specified text to the console as a simple log.
console.warn(strText): Output the specified text to the console as a warning.
console.error(strText): Output the specified text to the console as an error.
Both Firefox and Chrome provide ways to filter the console output so you can view just the types of output you want. Outputting text to the console has the virtue of not interrupting the execution of your script like an alert would. Now that you know about the console, you can execute any example in this book and replace the alert call with console.log and see the same output on the console, rather than as an alert.
Logging to the console provides a great way to keep an eye on the state of your scripts. One of the most common uses is to output the value of variables at different points in your script’s execution so that you can see how the variable changes. In Chrome and IE, you can output anything to the console, not just strings. If you output an object, you’ll see it enumerated on the console (see Figure 12):
Figure 12. Displaying an object in the Chrome browser console
In Chrome you can even drill down into an object to an arbitrary depth, including its prototypal inheritances. This is a great way to learn more about JavaScript and the DOM; try outputting the window object to the console and poking around in it.
In addition, you can interact directly with the console (see Figure 13). At the top of the Firefox console is an input field with a button labeled “Evaluate.” In IE’s console, it’s the input field at the bottom of the window marked with the >> symbol. In Chrome, you just click in the console window.
Figure 13. Entering JavaScript commands into the Chrome browser console
We are getting a reference to the body of the current document and then hiding it by setting its display property to none.
You can type in any valid JavaScript here and it will execute in the context of the page that’s been loaded into the browser window. As an experiment go to any page and type alert(document.title) into the console. (Usually you can just press Enter to cause the console to evaluate your code, but sometimes you have to click the Evaluate button in Firefox.) The browser window should alert the title of the page that’s loaded. If you enter window.document into the console and press enter (or evaluate), the document object should appear in the console. In Chrome, when you drill down into this object you’ll see HTML markup . . . and as you mouse over the elements Chrome will highlight the corresponding elements in the window. IE will only enumerate the first few properties on an object, and Firefox will only output the results of the toString() method of any object.
Breakpoints
One of the most useful tools for debugging scripts is the ability to set breakpoints in the code: specified points at which the browser stops executing the script and allows you to inspect the current state of the scripts and even change things. If you’ve never used breakpoints as part of your debugging process, we highly recommend giving it a try. It’s also useful for examining scripts to learn how they work.
Chrome’s developer tools support breakpoints and stepping through JavaScript code line by line. For an excellent tutorial, see https://developers.google.com/chrome-developer-tools/docs/scripts-breakpoints.
Firebug also supports breakpoints as part of script debugging. See https://getfirebug.com/wiki/index.php/Script_Debugging for details.
Loading Scripts Efficiently
As you start writing complex JavaScript applications, you’ll quickly find that one of the biggest performance issues you’ll have is slowness when your application first loads and initializes. Sometimes these problems are due to technical issues, like script blocking or inefficient download order, and sometimes these problems are purely perceptual: you can optimize your application loading process from a technical aspect, but that has side effects (like brief flashes of unmodified content) that result in your application seeming like it is slow, inefficient, or unpolished.
In this section we’ll discuss techniques that you can use that will be optimized for both actual speed and perceived speed. The first step is to talk about how browsers download and parse their assets, and then we’ll provide four common tips you can use to increase the loading efficiency of your application.
How Browsers Download and Process Content
The details of downloading and processing content aren’t specified in any standard, so browser manufacturers are free to implement any methodologies they prefer. As a result, there is some variation from browser to browser (and even from version to version of a given browser) on how that is implemented. Overall, though, there are some general commonalities:
Browsers parse HTML documents in order. As the browser parses a document, the elements it parses will become available in the DOM and it will begin downloading specified assets (images, external scripts, stylesheets, etc).
Browsers can download multiple assets in parallel. Older browsers will only download two assets in parallel, but newer browsers can handle up to six.
Loading an external JavaScript file will block other asset downloads. This is because a script might modify the DOM, or even redirect the browser to a different page, so to avoid unnecessary downloads the browser will not begin any other parallel downloads until the script is loaded, parsed, and executed.
Throughout this book our examples have illustrated document parsing order by always placing the JavaScript at the end of the HTML markup, just before the tag. This guarantees that the browser will have parsed the HTML before the script tries to access the elements. If you reverse the order, you’ll end up with a script trying to access an element that doesn’t exist yet, and usually that results in an error. To demonstrate, consider Listing 3 from Chapter 3:
Listing 3. Using getElementById()
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer’s Guide
</head>
<body>
<p id="myParagraph">This is my paragraph! <span class="hideme">Lorem ipsum</span> dolor
sit amet.
<p class="hideme">Another paragraph!
<script>
var myPar = document.getElementById("myParagraph"); // myPar is now a pointer to the paragraph.
myPar.innerText = "I have changed the content!"; // Change the text of the paragraph to "I have
changed the content!"
</script>
</body>
</html>
This simple example illustrates using the getElementByID() method and changing the text of a paragraph. If we place the <script> tag before the <p> tag, the example won’t work, as in Listing 1:
Listing 1. Trying to access a DOM element before it is available
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide
</head>
<body>
<script>
var myPar = document.getElementById("myParagraph"); // myPar is now a pointer to the paragraph. myPar.innerText = "I have changed the content!"; // Change the text of the paragraph to "I have changed the content!"
</script>
<p id="myParagraph">This is my paragraph! Lorem ipsum dolor
sit amet.
<p class="hideme">Another paragraph!
</body>
</html>
The script will not be able to access the paragraph, so getElementById() will return null. When we attempt to change the innerText property of null, the JavaScript engine will throw an error because null has no properties.
Also, in all of our examples our scripts are inline, meaning they’re actually in the document itself. As you write longer and more complex scripts, you’ll find it’s easier to keep them in separate files rather than placing them inline. Separate files make maintenance and collaboration with other programmers easier, and allow you to leverage deployment options like content distribution networks.
Unfortunately, when you load your scripts as separate files, it means that they will block the parallel download of assets that follow them in the document. That brings us to our first technique for optimizing your applications: load scripts at the end of the document.
Optimization Tip #1: Load Scripts at the End of the Document
One key technique (if an idea this simple can be called a technique) for optimizing your application loading process is to load scripts at the end of the document, after everything else has already been loaded. Then they won’t be in a position to block any other downloads, and any elements your scripts may need to access will be present in the DOM.
This technique can sometimes have an undesired side effect: Imagine you have a simple HTML document that you modify heavily with complex JavaScript. You’ve set up your document structure such that all your scripts are loaded at the end of the document. This results in a simple HTML document loading and appearing in the browser, and then the browser starts to load, parse, and execute your scripts. While the browser is doing that, your unmodified content is being displayed to the user and even worse because script loading is a blocking process, if the user attempts to interact with the page nothing will happen. Then the browser will finish loading, parsing, and executing your scripts and all of a sudden the browser will display the application as you intended. This “flash of unmodified (or unstyled) content,” brief though it may be, makes your application appear unpolished and inefficient even though technically your application has been optimized for loading efficiency.
If you find yourself dealing with a flash of unmodified content, remember that it’s a perceptual problem. The user is seeing something that they don’t expect and doesn’t help them, and that gives the perception that your application is at fault. In cases of perceptual inefficiencies, you can often turn it back to your advantage by providing the user with something that is helpful, like a loading screen. A loading screen tells the user that the application is still functioning; it’s just not ready yet. If your scripts are otherwise loading and executing efficiently, that’s usually enough to buy your application the time it needs to get going.
Creating a loading indicator is easy; just include the markup for it at the very beginning of your document, so that it’s the first thing that is parsed and displayed. It can be a simple box in the middle of the screen with the message “Loading…” or it can be a small image. Typically a loading indicator is absolutely positioned in the middle of the screen and blocks access to elements underneath it. To dismiss it all you have to do is place a simple call at the end of your last script to access the loader in the DOM and either remove it or, if you might want to use it again later, to simply hide it.
Here’s an example that simulates a loading delay with a timer. In Listing 2, we’ve got a simple loading indicator that covers everything on the screen, and then when the application is “ready” (in this case, when the timer is up) the script hides it:
Listing 2. A simple loading indicator
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide
<style>
body, h1 {
margin: 0;
padding: 0;
}
#container-loading {
position: absolute;
/*
* Radial gradient CSS generated by CSS Background Maker
* http://ie.microsoft.com/testdrive/graphics/cssgradientbackgroundmaker/default.html
*/
/* IE10 Consumer Preview */
background-image: -ms-radial-gradient(center, circle farthest-corner, #9E9E9E 0%, #141414 100%);
/* Mozilla Firefox */
background-image: -moz-radial-gradient(center, circle farthest-corner, #9E9E9E 0%, #141414 100%);
/* Opera */
background-image: -o-radial-gradient(center, circle farthest-corner, #9E9E9E 0%, #141414 100%);
/* Webkit (Safari/Chrome 10) */
background-image: -webkit-gradient(radial, center center, 0, center center, 506,
color-stop(0, #9E9E9E), color-stop(1, #141414));
/* Webkit (Chrome 11+) */
background-image: -webkit-radial-gradient(center, circle farthest-corner, #9E9E9E 0%, #141414 100%);
/* W3C Markup, IE10 Release Preview */
background-image: radial-gradient(circle farthest-corner at center, #9E9E9E 0%, #141414 100%);
}
#container-loading div {
width: 200px;
height: 100px;
border: 2px solid #ccc;
background-color: #fff;
text-align: center;
line-height: 100px;
font-family: arial, helvetica, sans-serif;
font-weight: bold;
font-size: 1.5em;
border-radius: 20px;
position: absolute;
}
</style>
<script>
function showLoading(boolShow) {
var loadingIndicator = document.getElementById("container-loading"),
loadingMessage = loadingIndicator.querySelector("div");
if (boolShow === true) {
// Show the loading indicator.
// Position everything
loadingIndicator.style.width = window.innerWidth + "px";
loadingIndicator.style.height = window.innerHeight + "px";
loadingMessage.style.left = ((window.innerWidth - 200) / 2) + "px";
loadingMessage.style.top = ((window.innerHeight - 100) / 2) + "px";
loadingIndicator.style.display = "block";
} else {
loadingIndicator.style.display = "none";
}
</script>
</head>
<body>
<!-- Begin: Loading Indicator -->
<div id="container-loading">
<div>
Loading...
</div>
</div>
<script>
// Initialize and show the loading indicator.
showLoading(true);
<!-- End: Loading Indicator -->
<!-- Begin: Rest of document -->
<h1>Hello World!</h1>
<!-- Pretend complex markup goes here -->
<!-- End: Rest of document -->
<!-- Begin: loading scripts -->
<!-- Script(s) loaded here -->
<!-- End: loading scripts -->
<script>
// Simulate a loading delay with a timer
setTimeout(function() {
showLoading(false);
}, 2000);
</script>
</body>
</html>
Note that in this loading indicator we do include two bits of inline JavaScript to handle positioning and layout, as well as hiding and showing the indicator. The first bit of inline JavaScript is in the head of the document, and it sets up a showLoading() function that we can use to show or hide the loading dialog. We could easily have included the function definition in the body, after the loading dialog markup, but the function doesn’t need to follow the markup in order to be defined because it doesn’t access the elements until it is called. If we define the function in the head, but don’t call it until after the elements it accesses are parsed, it will work as expected. We could even pull this simple script out into a separate file and load it using a non-blocking technique if we wanted; see Tip #3.
The other bit of inline JavaScript simply calls the showLoading() function to initialize the loader and show it correctly. Because this script does only one thing, in the name of efficiency it makes sense to keep it inline in the document, rather than pulling it out into a separate file (pulling it out into a separate file would also cause it to block the loading of the complex document that came after).
Loading screens are an easy solution to the problem of blocking scripts, but they come with a couple of caveats. First, they’re not really appropriate for every situation. If you’re building an informational website, for example, you certainly don’t want to make people wait for your site to load. On the other hand, if you are building a complex data-driven web application, a loading indicator can be quite useful.
Second, you should always try to minimize the loading delays so that you’ll never need a loading indicator, or so that it will only be on the screen for as brief a time as possible. Our next optimization tip will help you achieve that goal.
Optimization Tip #2: Combine, Minify, and GZip
A common technique for increasing the efficiency of JavaScript applications is to combine scripts into as few files as possible. This reduces the number of HTTP requests the browser has to make, and can have a big impact on the loading speed of your application. Even if you only have a dozen or so JavaScript files, it would be a good idea to look through them and decide which ones could be combined.
If you’re hesitant to combine your scripts into one file because it will make them difficult to maintain, that’s a legitimate concern. That’s why many projects have a publication step that takes a development version of the application and combines the JavaScript files together into one file. That way developers get to keep the more manageable separate files, but the user doesn’t have to suffer the extra loading times they would entail. Note that when you do this, it means that the environment you and your fellow programmers are working in is not the same as what your users will be experiencing. The users will be experiencing a more optimized environment, which could result in problems you will not witness. You should test your application regularly in a “compiled” production version to avoid being surprised by bugs at time of deployment.
Another common deployment optimization technique is to minify the combined JavaScript files. Minification is a method where an automated parsing program loads a JavaScript file and then attempts to reduce the overall size of the file by removing comments, removing unnecessary whitespace, and replacing long variable, property, and method names with smaller ones. The result is JavaScript that is very difficult for humans to read, but which browsers have no problem understanding and which is significantly smaller in filesize, thus reducing download times.
Finally, JavaScript files are often compressed using the GZip compression algorithm before they’re deployed. Browsers have the ability to accept a GZipped JavaScript file and unzip them internally before processing them. Even though this introduces an extra step for the browser to perform, the time it takes is generally much less than the time it would take for an uncompressed file to be transmitted to the browser, resulting in an overall performance increase. GZip is only applicable for files above 100 bytes or so; files smaller than that will actually get bigger because GZipping does involve some overhead in the file. If your file is big enough, though, the overhead will not be noticeable in comparison to the size savings.
You can just minify or just GZip your JavaScript files, but the best savings in size (and therefore network transmission time) are when you do both. There are several tools available for minifying JavaScript:
Google’s Closure Compiler: Google’s Closure Compiler doesn’t just minify your JavaScript, it will also optimize it. It removes unused code paths, rewrites inefficient code, and then minifies the results. The output is often much smaller than plain minification, and will run measurably faster. See https://developers.google.com/closure/compiler/ for details on using the Closure Compiler in your project.
JSMin: One of the oldest minifiers available, but still very good, is Douglas Crockford’s JSMin, available at http://www.crockford.com/javascript/jsmin.html. It just minifies code, it does not rewrite inefficient code like the Closure Compiler, so provides a slightly less heavy-handed approach. It’s written in C, but is easy to build and integrates well into deployment scripts.
YUI Compressor: Built by Yahoo and part of their YUI library, YUI Compressor is a great choice for minifying your JavaScript. As an added bonus it can also minify CSS files. YUI Compressor can also be included in a Node script, making it super-easy to write your own deployment scripts using JavaScript! See http://yui.github.com/yuicompressor/ for details on YUI Compressor.
GZipping your files is something you can either do by hand as you deploy, or most modern web servers can be instructed to automatically compress certain file types before transmitting them. The Apache module mod_deflate (see http://httpd.apache.org/docs/2.2/mod/mod_deflate.html) makes it easy to specify what file types to compress, or even which browsers to compress for. If you’re using Xampp, it can be configured to use mod_deflate by uncommenting the appropriate directive in the configuration files, see http://stackoverflow.com/questions/6993320/how-to-enable-gzip-compression-in-xampp-server for details. For other servers check the documentation to see what’s possible.
Optimization Tip #3: Load Scripts In the Document Head Using a Non-Blocking Technique
In Tip #2 we had a small bit of inline script in the head of the document that defined the function for hiding and showing the loading indicator. It was safe to include in the head of the document because it was just a function definition, and did not access the DOM as part of its definition. As you write your scripts, you’ll find a significant portion of them will fall into the category of JavaScript that could be loaded in the head of the document because it doesn’t access the DOM until called. JavaScript libraries often fall into this category.
If we could load those scripts in the head, we wouldn’t have to load them at the end of the document, and that would reduce the amount of time the users would have to look at our loading dialog and maybe even eliminate the need for one entirely. But if we load them as separate files in the head, they’ll block anything that comes after.
Fortunately, there is a way to load scripts asynchronously. The reason why scripts block other assets from loading is because they’re included as part of the regular document flow. If we inject scripts into the document, they won’t be a part of the regular document flow. The browser will still load, parse, and execute them, but they won’t be blocking other assets from loading.
Script tags can be injected into a document just like any other DOM element. You just create a new <script> tag, set its src property, and then append it to the DOM. The moment you append it to the DOM, the browser will begin downloading the script.
As an example, imagine we had several functions we wanted to define—maybe our own personal JavaScript library. And we had several predefined data objects we would like to create, which are defined in a separate file, and maybe there’s an analytics package we want to load as well as a third party library being served from a content distribution network. None of these scripts require the DOM in order for them to be executed (though any of them might define methods that when called would access the DOM—but as long as we don’t call those methods until the DOM is ready we’ll be okay) so we can load them in the head. Listing 3 is an example script called manifest-loader.js that would do all of that:
Listing 3. manifest-loader.js
// Define a manifest array of scripts to load
var defaultManifest = [
"scripts/js-lib.js",
"scripts/js-objects.js",
"scripts/third-party/omniture.js",
"http://big.cdn.com/useful-library.js"
]
// Define a function to load a manifest array.
function loadManifest(arrManifest) {
var i,
arrManifestLength = arrManifest.length;
for (i = 0; i < arrManifestLength; i++) {
var newScript = document.createElement("script");
newScript.src = arrManifest[i];
document.getElementsByTagName("head")[0].appendChild(newScript);
}
}
In this example we first create a manifest array of all the URLs we want to load asynchronously. Then we define a function that, when given a manifest array, will loop through the array, create a new script tag, set the src attribute, and then append the script tag to the head of the document, thus causing the script to load asynchronously.
Now we can just load this script into the head of our hypothetical document. We can also include an even smaller inline script that calls loadManifest(defaultManifest) and all of our scripts will begin loading without blocking the rest of the document, as shown in Listing 4:
Listing 4. Using the manifest loader
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide
<script src="scripts/js-loader.js">
<script>
loadManifest(defaultManifest);
</script>
</head>
<body>
<h1>Hello World!
</body>
</html>
It’s very important that none of these scripts in the manifest need the DOM to be ready, because it almost certainly not be available when they’re loaded. Note that it is possible to determine when the document is ready, but the details vary greatly from browser to browser and even version to version of a given browser. However, it’s a common functionality that many JavaScript libraries provide (like jQuery, see below).
If you don’t want to build this sort of infrastructure for yourself, you can always look for the features you need in an existing JavaScript library. For example, RequireJS (http://requirejs.org/) takes this technique one step further and allows you to specify dependencies in your JavaScript files, which can be dynamically loaded on an as-needed basis.
Optimization Tip #4: Moderation is Good
Don’t fall into the trap of thinking that you absolutely must use any (or all) of these techniques. As you are optimizing your application loading process, you should always look at what makes the most sense for your specific situation. In our loading indicator example above, we kept some scripts inline in the document rather than loading them from external files. We did this because placing them where they are guarantees that they’ll be available when we need them (right after the browser parses the necessary part of the document) and because they are small enough that optimizing them wasn’t really necessary.
In your projects you should experiment to see what combination of techniques provides the best results. You’ll probably find that you’ll use a combination of these techniques to balance development needs with deployment requirements across all your target browsers.
As mentioned in Tip #3, if you don’t want to build this sort of optimization infrastructure yourself you can always look for the features you need in a JavaScript library or framework.
Asynchronous Communication using XMLHttpRequest
In addition to loading and displaying pages based on user requests (or JavaScript commands), browsers provide another method for accessing servers: the XMLHttpRequest object. This object provides an interface for JavaScript to make requests to web servers and process the responses as needed. The primary difference between this interface and simply loading a page is that these requests can be made asynchronously—they run in the background and will not block user interactions with the main page. In addition, the results of these requests can be processed by JavaScript and incorporated into the current document as needed. Using this technique it is therefore possible to create user interactions that do not require page refreshes, enabling a much more application-like look and feel for a web application.
As you might think from the name XMLHttpRequest, the request can involve XML. And indeed when the technique was first implemented, XML was the preferred way of encoding information. However, the technique can involve any encoded data that is valid for transmission across HTTP: HTML, JSON, or even plain text. The technique also has another name: Asynchronous JavaScript and XML, or AJAX for short.
Originally implemented by Microsoft, the XMLHttpRequest technique was so useful that other browsers quickly implemented it, and it is now a part of the DOM standard at http://www.w3.org/TR/XMLHttpRequest/
How It Works
The XMLHttpRequest object performs all of the network access for you, going through all of the necessary steps to contact the server and communicate correctly with it. As it does its work, it will dispatch events at various points—when it has finished communicating with the server, for example, or if an error has occurred. You provide event handlers for those events to correctly process the results.
The basic steps are as follows:
Create the desired event handler functions.
Create a new instance of the XMLHttpRequest object and configure it for use (including providing the event handlers).
Instruct the XMLHttpRequest object to make the request. As it does its work, it will dispatch events at the appropriate times.
Let’s go over each of those steps one at a time.
Step 1: Event Handlers
As it goes through its work, the XMLHttpRequest object will dispatch events for which you can provide handlers. As with the DOM interaction events (like click or keypress), the event handler will be called when the event occurs, and will be passed an event object which will contain information about the event. The event types are:
readystatechange: This event is fired multiple times as the XMLHttpRequest object does its work. When fired, this event will provide a readyState property on the event object that will have one of the following values:
0: Unsent, meaning the XMLHttpRequest object has been created but the request has not been sent.
1: Opened, meaning the open() method has been called and has successfully completed (and thus a request is underway).
2: Headers received, meaning all headers have been received from the server (this includes redirect headers, if any). At this point the status property of the event object is now available, and will have the HTTP response code from the server.
3: Loading, meaning the response is being loaded.
4: Done, meaning the response has been successfully loaded. The response from the server is now located in the responseText property of the event object.
abort: Fired when the request has been aborted (as when the XMLHttpRequest.abort() method is called).
error: Fired when an error condition has occurred.
load: Fired when the request has completed successfully.
loadend: Fired when the request has completed regardless of success.
loadstart: Fired when the request is sent.
progress: Fired during the loading process as it progresses.
timeout: Fired if a specified timeout period elapses before the request could be completed.
All of these events except the readystatechange event are new to the most recent version of the XMLHttpRequest specification, and so they may not be fully implemented in all of your target browsers. (Webkit, for example, only just implemented the timeout event; see https://bugs.webkit.org/show_bug.cgi?id=74802.) The readystatechange event is the legacy event specified by the original specification and is widely implemented, and it provides all the information we need for handling most success and error situations.
A good readystatechange event handler uses the readyState property to see how far the request has progressed and uses that to act accordingly. It will also use the status property to check for HTTP status messages (like status 404 for file not found). We can see this in Listing 5:
Listing 5. A simple readystatechange event handler
function handleReadyStateChange(objXHR) {
if (objXHR.readyState === 4) {
// Request is done. Was the requested file found?
If ((objXHR.status !== 200) && (objEvent.status !== 304)) {
// Something happened..possibly the requested file wasn't found?
// Tell the user that something went wrong.
console.error("An error occurred while processing the request.");
} else {
// The requested file was found and sent and the content is now available in
// the objXHR.responseText property:
console.log(objXHR.responseText);
}
}
}
We assume this event handler will be called multiple times as the request progresses, but the only time we want to do anything is if the readyState property is 4, meaning the request is Done. Then we check the HTTP status that was returned by the server, located in the status property. We’re going to assume that any status other than a 200 (success) or 304 (resource not modified) is an error condition; however you could do more detailed error handling based on different status codes if you wanted. (See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for a full list of status codes, their meanings, and when they are provided.) Assuming the request was a success, we simply output the result to the console. (Obviously you can do much more with the result than just send it to the console; this is a simple example to illustrate the pattern.)
Step 2: Creating and Configuring the XMLHttpRequest Object
The browser’s XMLHttpRequest object is an abstract object, meaning it isn’t accessed directly but rather is used as a pattern to create new instances. It’s these new instances that you use in your scripts to perform the asynchronous requests. You can even have more than one.
To create a new XMLHttpRequest object, you simply use the new keyword as with any JavaScript constructor:
var myXHR = new XMLHttpRequest();
This syntax is valid in all browsers that support this feature with the single exception of Internet Explorer 6. For IE6, the feature is still available but involves evoking a new ActiveX object.
Once you have a new object, you will need to configure it. At the very least, an XMLHttpRequest object needs to know a URL to request, what HTTP method to use to make the request (typically GET or POST, but you can also do HEAD requests if you just want to examine the headers of a URL without actually fetching it), any supporting data needed (e.g. form data in the case of a POST), and at least one event handler to execute in case of success.
You start by opening the object using the open() method:
myXHR.open(strMethod, strURL, boolAsynchronous);
strMethod: The HTTP method to use for the request
strURL: The desired URL to use
boolAsynchronous: A boolean indicating whether or not to perform the request asynchronously. This parameter is optional; if omitted it defaults to true. (If set to false this will cause the request to be performed synchronously, and it will pause the browser for the duration of the request just like a regular page load.)
Then, once you have opened the object, you can register your event handler. The most common choice is to register an event handler on the readystatechange event:
myXHR.onreadystatechange = function() {
handleReadyStateChange(myXHR);
};
This registers our previously-defined handleReadyStateChange() function as the event handler for the readystatechange event. (If you are working only with browsers that implement the latest version of the standard, you can also use the addEventListener() method, just as if this were any other DOM event.)
Step 3: Sending the Request
Sending the request is easy. All you have to do is call the send() method on the configured object:
myXHR.send(postData);
postData: Data from a form that is being posted as part of the request. In the case of GET requests, this will always be null.
At that point the object will perform the request and trigger event handlers as outlined.
Note : You can send an XHR request to any server, even one that is on a different domain from the source of your page. However, access to the request result is limited by the Same Origin Policy. Unless the origin of the response and the origin of your script match, your script will be unable to access the response. The “Cross Domain Methods” section of this chapter discusses some techniques for safely getting around these limitations.
Putting It All Together
It’s pretty simple to configure and send an XMLHttpRequest, but we can make it even easier in our scripts if we create a single function that can do it all for us.
There are a lot of things we need for a robust XMLHttpRequest object: a URL, the method, any post data, and callbacks for success, error, and timeouts. We could provide each of those things as a separate parameter in a single function, but it would be tidier to gather them all up into a single object and then provide that object as a single parameter to the function, as in Listing 6:
Listing 6. A configuration object for an XMLHttpRequest function
var myXhrDefs = {
strMethod : “GET”,
strUrl : “http://myhost.com/ajax-test.txt”,
intTimeout: 3000, postData: null,
boolAsync: true,
successCallback: function(objXHR) {
// Do things when the request is successful
console.log(objXHR.responseText);
},
errorCallback: function(objXHR) {
// Do things when there is an error in the request.
console.error(“The XHR failed with error “, objXHR.status);
},
timeoutCallback: function() {
// Do things when a timeout occurs.
console.error(“The XHR timed out.”);
}
}
Here we have gathered everything up into one object, even the callbacks which are methods on the object. In Listing 7, we can then build a function that uses this object as a parameter to handle our XMLHttpRequests:
Listing 7. An XMLHttpRequest function
function doXHR(myXhrDefs) {
// Create and configure a new XMLHttpRequest object
var myXhr = new XMLHttpRequest(),
myTimer = null;
myXhr.open(myXhrDefs.strMethod, myXhrDefs.strUrl, myXhrDefs.boolAsync);
// Register the error and success handlers
myXhr.onreadystatechange = function() {
// If readyState is 4, request is complete.
if (myXhr.readyState === 4) {
// Cancel the timeout timer if we set one.
if (myTimer !== null) {
clearTimeout(myTimer);
}
// If there's an error, call the error callback,
// Otherwise call the success callback.
if ((myXhr.status !== 200) && (myXhr.status !== 304)) {
if (myXhrDefs.errorCallback != null) {
myXhrDefs.errorCallback(myXhr);
}
} else {
myXhrDefs.successCallback(myXhr);
}
}
}
// Handle timeouts (set myXhrDefs.intTimeout to null to skip)
// If we're working with a newer implementation, we can just set the
// timeout property and register the timeout callback.
// If not, we have to set a start running that will execute the
// timeout callback. We can cancel the timer if/when the server responds.
if (myXhrDefs.intTimeout !== null) {
if (typeof myXhr.ontimeout !== “undefined”) {
myXhr.timeout = myXhrDefs.intTimeout;
myXhr.ontimeout = myXhrDefs.timeoutCallback;
} else {
myTimer = setTimeout(myXhrDefs.timeoutCallback, myXhrDefs.intTimeout);
}
}
// Send the request
myXhr.send(myXhrDefs.postData);
}
In this example, we’re creating a function that can handle all of our XMLHttpRequest needs. It creates and configures a new XMLHttpRequest object and then registers the provided event handlers as needed. To browsers that do not yet implement the timeout feature for XMLHttpRequest, it implements a timer using setTimeout(). Then it sends the request for us. All we have to do to use the function is set up an object and then call the function:
doXHR(myXhrDefs);
Pulling all of our XMLHttpRequest code into one function makes maintenance and upgrading easier; instead of manually managing XMLHttpRequest objects in possibly multiple places in our code, we call it in only one spot.
Cross Domain Techniques
All browsers implement a security measure called the Same Origin Policy. This policy allows scripts on a page to access each other’s properties and methods as long as they are all served from the same origin: the same protocol (http or https), the same host name, and the same port number (if specified). Scripts from different origins are not allowed to interact with one another. Without this policy malicious scripts could access our pages and data.
Unfortunately, this policy also stands in the way of easily making applications that work with data from multiple origins. There are a few methods for getting around this limitation, however, and because this is a common barrier in JavaScript development we wanted to cover them here briefly.
Note that all of these techniques involve potential security risks, and what risks are involved will vary depending on how you employ them. You should evaluate these techniques carefully for security problems before employing them, to avoid malicious scripts from gaining access to your pages or your data.
Server-side proxy
One of the easiest solutions to implement is to create a simple proxy service that runs on your domain, and use that to do all of your cross-domain queries. From the browser’s standpoint all requests are coming from the same origin, so everything is okay.
You can implement the server-side proxy in any language you want, including JavaScript (thanks to node.js). In fact, there are already several proxy servers built using node. We’ve built simple proxies using both PHP and Java. For a great example, see Ben Alman’s Simple PHP Proxy, available at http://benalman.com/projects/php-simple-proxy/
JSONP
JSONP stands for “JSON with padding,” a rather confusing name for a technique that gets around the Same Origin Policy by making use of script injection.
One of the exceptions to the Same Origin Policy is that scripts loaded through a <script> tag with a src attribute are exempt, and can access and be accessed by scripts from another origin. For an example of this, see the examples in the Using jQuery section, below, where we load jQuery from a content distribution network, which is definitely a different origin than the example pages. If it weren’t for this exception, we wouldn’t be able to do this.
Ordinarily when you query a service you’d get back a JSON string as a response. You’d then use JSON.parse() to deserialize the string into an object, and then access the properties on the object to do what you need. The Same Origin Policy prevents this if the service is in a different origin than the querying page.
But what if the service didn’t return a simple JSON string, and instead returned a script that could be injected into the querying page? If we did that, the injected script would be exempt from the Same Origin Policy, and we’d be able to access its properties and methods as desired. That’s the key to JSONP: the service doesn’t return straight JSON for parsing, it returns JSON padded inside a script that is injected into the host document.
There are two different kinds of padding that are typically used in JSONP. One is that the returned script simply does a variable assignment, like so:
myResponse = { “foo” : bar, “serial” : 238 };
If that’s injected into the host document via a script tag, the variable myResponse would become available in the global scope, enabling you to access the properties as needed. The other way is for the returned script to execute a function call, like so:
responseHandler({ “foo” : bar, “serial” : 238 });
This executes the responseHandler() function with the desired data as an object literal. (This assumes you have defined a function responseHandler() already, so that the injected script can call it, of course.)
Which kind of padding you will need will typically be specified by the target service. You, in turn, will specify the name of the variable or function in your query to the target service when you inject the script tag, like this:
<script src=“http://www.service.com/getserial?jsonp=myResponse”></script>
<script src=“http://www.service.com/getserial?jsonp=responseHandler”></script>
The exact syntax will be provided by the service; it may not be “jsonp=varname” or “jsonp=functionname”. (Twitter, for example, specifies that the format should be “callback=functionname”—see https://dev.twitter.com/docs/things-every-developer-should-know#jsonp.)
Step by step, here’s how it goes:
If the service specifies that it will pad the JSON with a function call, create a function that you can use to handle the data. If the service specifies that it will pad the JSON with a variable assignment you can namespace the variable if you wish.
Create a new <script> tag. Update its src attribute to have the target URL formatted as the service specifies.
Append the <script> tag to the document. This will cause the browser to go fetch its URL, resulting in a cross-domain call.
Note that this will mean you’ll be creating a new <script> tag with every JSONP call. If you’re only making a few calls (maybe a dozen or less) that will probably be okay. But if you’re relying heavily on JSONP, you’ll want to re-use your <script> tags so you don’t end up with a bloated DOM and the resulting memory problems. The easiest way to do this is to write a simple function that can handle all of your JSONP calls, as in Listing 8:
Listing 8. A function that executes JSONP calls and recycles the script tag
function executeJSONPQuery(strUrl) {
// Check to see if a jsonp script tag has already been injected.
// Also, create a new script tag with our new URL.
var oldScript = document.getElementById(“jsonp”),
newScript = document.createElement(“script”);
newScript.src = strUrl;
newScript.id = “jsonp”;
// If there is already a jsonp script tag in the DOM we'll
// replace it with the new one.
// Otherwise, we'll just append the new script tag to the DOM.
if (oldScript !== null) {
document.body.replaceChild(newScript, oldScript);
} else {
document.body.appendChild(newScript);
}
}
This example function takes a URL as a parameter. It creates a new script tag with the URL as its src attribute and “jsonp” as its ID. It then looks to see if there is another script tag with that ID. If it finds one, it replaces it with the new one, otherwise it simply injects the new script tag into the DOM. Either way, adding the new script tag to the DOM causes the browser to go load the specified URL as a script.
A great example for using this is Twitter. They provide a robust but simple API for searching tweets that will provide responses in JSONP format. For a simple example, let’s just show the last 20 tweets from the author Jon Reid. We’ll build a function called handlejsonpresults() which will receive an object literal as its parameter. According to the Twitter API documentation, the object will have an array called “results” as one of its properties. Each result is an object representing a single tweet, and has a property called “text” that contains the text of the tweet. In Listing 9 we’ll loop through the array and create a list item for each tweet, and then append the results to the document:
Listing 9. Fetching tweets from the Twitter API and displaying them in the document
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”></script>
</head>
<body>
<h1>Hello World</h1>
<p id=“clickme”>Click here for tweeting goodness!</p>
<script>
// Attach a click event handler to the paragraph so that it will load
// the tweets.
var clickme = document.getElementById(“clickme”);
clickme.addEventListener(“click”, function(event) {
executeJSONPQuery(“http://searcexact syntax will be provided by the servim:jreid01&callback=
handlejsonpresults”);
});
// Execute a JSONP query, reusing the script tag.
function executeJSONPQuery(strUrl) {
// Check to see if a jsonp script tag has already been injected.
// Also, create a new script tag with our new URL.
var oldScript = document.getElementById(“jsonp”),
newScript = document.createElement(“script”);
newScript.src = strUrl;
newScript.id = “jsonp”;
// If there is already a jsonp script tag in the DOM we'll // replace it with the new one.
// Otherwise, we'll just append the new script tag to the DOM.
if (oldScript !== null) {
document.body.replaceChild(newScript, oldScript);
} else {
document.body.appendChild(newScript);
}
}
// This function is called by the injected scripts.
function handlejsonpresults(objData) {
var arrTweets = objData.results,
arrTweetsLength = objData.results.length,
i,
myNewList = document.createElement(“ul”);
// Loop through the results array and create a list item for
// each tweet containing its text.
for (i = 0; i < arrTweetsLength; i++ ) {
var myLi = document.createElement(“li”),
myTextNode = document.createTextNode(arrTweets[i].text);
myLi.appendChild(myTextNode);
myNewList.appendChild(myLi);
}
// Now that all of the tweets have been compiled, append the list to the DOM.
document.body.appendChild(myNewList);
}
<
</body>
</html>
In this example, every time you click on the paragraph the script will execute a JSONP call using our function (and thus recycling the script tag) and append the results to the page.
JSONP is useful, but it’s not without its drawbacks. The biggest problem is that it necessarily involves a security risk. You’re trusting the target URL to not send a malicious script in response to your request. If the target URL responds with a malicious script, you would inject it into your own page and not even know it. Currently there is no effective way to close that particular security hole, so be very sure you want to open it.
CORS
Created in response to the security concerns with JSONP, CORS is a proposed standard that is gaining some traction. An acronym for Cross Origin Resource Sharing, CORS specifies new HTTP headers that browsers and servers must provide as part of their communication with one another. Though many of the latest versions of browsers support CORS headers, older ones do not, making this a poor choice for projects that will be targeting even slightly out of date browsers.
Post Message
HTML 5 provides a new feature that can be used for cross-domain communication: the Post Message standard. This standard specifies a new DOM method, window.postMessage(), and a new DOM event, message, that are used to communicate between iframes.
An iframe can load a document from a different origin than the host document, but the Same Origin Policy prevents scripts from either document interacting with each other. Post Message, however, provides a secure means for transmitting strings between them.
In the sending document, your script can call the postMessage() method on a pointer to the target document, which can be either a child document or a parent document. The postMessage() method takes as a parameter the string to be transmitted to the target document, along with the desired origin of the target document.
When the target document receives the message, it triggers a DOM message event. You can create a handler for this event and register it to the window object. The event object passed into the handler will contain the string that was sent along with the origin that was specified. You can then make sure that you are received the message from the expected origin.
The Post Message method is widely supported among modern browsers. Older browsers support it as well; Internet Explorer 8 and greater support it, as does Firefox 16+, Chrome 23+, and Safari 5.1+. Post Message enjoys even more support on mobile browsers: Safari Mobile on iOS has supported it since 3.2, and the Android browser has supported it since 2.1.
We cover the postMessage() method in detail in the Chapter 8 under window.postMessage(). See that section for details on syntax and an example.
Data Caching
As you do more asynchronous programming with XMLHttpRequest, it’s not uncommon to find yourself wanting to cache data locally for speed and efficiency. This is particularly true with mobile applications, where having a local cache of data can not only speed up your application (because accessing cached data is faster than going over the potentially quite slow network) but also make your application use less battery power (because accessing the network requires power). Even for desktop applications it’s common to want to cache commonly used information that doesn’t change very often.
Data caches are very simple, and typically consist of an identifier representing the service or data source, a timestamp of the last time the service was accessed, and the data that was returned the last time the service was accessed. Every time you look at the cache, you can see if the data you want is cached; if it isn’t, you can simply call the service and cache it with a new timestamp. If there is data in the cache, you check its timestamp. If it was accessed too long ago, you’ll know you need to access the service and cache the new results. But if it wasn’t accessed too long ago, you can just use the cached data.
We’ve already built a doXHR() function in the Asynchronous Communication using XMLHttpRequest section above. When we call that method, we provide an object that specifies the URL to call, as well as other information (such as a success method to call, or an error method to call). What would be nice is if we specified a length of time as a property, the doXHR() function would know that we want to cache the data from that URL and it could perform as follows:
Check to see if there is already data in our hypothetical cache.
If the data does not exist in the cache, go and fetch the data from the URL, and save it in the cache with the current timestamp.
If the data does exist in the cache, check the timestamp.
If the timestamp is too old, go and fetch the data from the URL and save it in the cache with the current timestamp.
If the timestamp isn’t too old, just use the data from the cache.
We can easily extend our doXHR() function to handle caching. In our example, we’ll use the localStorage feature defined by HTML 5, because it is widely supported (especially among mobile browsers). As covered in detail in Chapter 8, window.localStorage takes key/value pairs and stores them in a cache that persists even if the user closes their browser or reboots their computer, which makes it an ideal choice for our data cache.
We’ll begin by extending our XHR definition object in Listing 10 to include two new properties: A name for the service we’re caching, and a duration for when the cache is valid:
Listing 10. An updated version of the XHR definition object
var myXhrDefs = {
intCacheDuration: 14400,
cacheName: “ajax-test”,
strMethod : “GET”,
strUrl : “http://127.0.0.1:8020/developers-guide/chapter-4/ajax-test.txt”,
intTimeout: 3000,
postData: null,
boolAsync: true,
successCallback: function(objEvent) {
// Do things when the request is successful
console.log(objEvent.responseText);
},
errorCallback: function(objEvent) {
// Do things when there is an error in the request.
console.error(“The XHR failed with error “, objEvent.status);
},
timeoutCallback: function() {
// Do things when a timeout occurs.
console.error(“The XHR timed out.”);
}
}
The two new properties are intCacheDuration (which we will specify in seconds; 14400 seconds is four hours), and cacheName, which provides a base name for the cached data. When we cache the information, we’ll append “-timestamp” to cacheName for the key, and when we cache the data, we’ll append “-data” to cacheName for the key. (So in this example, our sample URL of ajax-test.txt will use “ajax-test-timestamp” for its timestamp key and “ajax-test-data” for its data key.)
To make use of this new object, in Listing 11 we’ll wrap our existing doXHR() method within another method, which we’ll call doCachedXHR(). This will take the same XHR definition object and either read from the cache or use doXHR() to fetch new information and cache it:
Listing 11. The doCachedXHR() method
// Either perform an asynchronous call to a service and cache it with a timestamp,
// or if the service has already been called and cached, just use that if the data isn't
// too old. If the data is too old, perform the asynchronous call again and cache the
// results with a new timestamp.
function cachedXHR(myXhrDefs) {
var fetchNewData = false,
now = new Date(),
lastTimeStamp = localStorage.getItem(myXhrDefs.cacheName + “-timestamp”);
// Does the cache even have the specified item?
if (lastTimeStamp == null) {
fetchNewData = true;
} else {
// We've cached the service at least once. Check the last timestamp.
var timeStamp = new Date(lastTimeStamp);
if ((timeStamp.getTime() + (myXhrDefs.intCacheDuration * 1000)) < now.getTime()) {
fetchNewData = true;
}
}
// If we need to fetch new data, we need to extend the existing successCallback method
// to cache the new results with a new timestamp.
if (fetchNewData) {
myXhrDefs.successCallback = (function(oldCallback) {
function extendedCallback(objEvent) {
localStorage.setItem(this.cacheName + “-data”, objEvent.responseText);
localStorage.setItem(this.cacheName + “-timestamp”, now.toISOString());
oldCallback(objEvent);
}
return extendedCallback;
})(myXhrDefs.successCallback);
// Perform the XHR request.
doXHR(myXhrDefs);
} else {
// Just use the cached data.
var cachedData = localStorage.getItem(myXhrDefs.cacheName + “-data”),
fakeEvent = {
responseText : cachedData
};
myXhrDefs.successCallback(fakeEvent);
}
}
There’s a lot going on in this method, including a trick for extending methods, so let’s walk through it one step at a time.
At the heart of the method is a simple check: is the data cached or not? If it isn’t cached, the method needs to go get the data and cache it. If it is cached but too old, it needs to refresh the cache. If the data isn’t too old, we can use it. That simple logic makes up the skeleton of the method.
The first thing we need to do is create a new Date object representing the current time. Then we check to see if the data has even been cached by looking for a timestamp. If the timestamp doesn’t exist, we know we need to fetch and cache the data.
If the timestamp does exist in the cache, we need to check to see if it’s too old. First, we create a new Date object using the timestamp. Then we can use the getTime() method of the date objects to perform our age comparison—the getTime() method returns the number of milliseconds from midnight, January 1, 1970, so we’ll have to convert our intCacheDuration time from seconds to milliseconds before adding them to the timestamp. (This is a great practical example of comparing the values of Date objects.)
If the data isn’t too old, we can just use it. But if it is too old, we have to fetch new data. We’ve defined our successCallback() method on the XHR definition object, but we want to extend that method so that it caches the result of the XHR request. Basically what we do is we overwrite the old successCallback() method with the results that are returned from an immediately executed function expression (IEFE). We pass the old successCallback() method into the IEFE as the oldCallback parameter. So within the IEFE we create a new function that caches the data, then calls the oldCallback function. The IEFE then returns that new function, which takes over the successCallback() method.
We could have just re-written the successCallback() methods in our XHR definition objects, but with this technique you can add the new cachedXHR() method to existing code and not have to modify a bunch of your methods to account for caching. The new method will handle that for you.
We encourage you to test this function to verify that it works the way you expect. You’ll need to set up your own personal web server—in this example, we’re just using the debugging server that comes with Aptana, which runs on port 8020. We’re also using a simple ajax-test.txt file that contains the text “hello world.” But you could easily transmit JSON-formatted data, or XML-formatted data.
This function is pretty basic, and could use some more features. For example, what if the browser doesn’t support localStorage? You could easily extend this function to use document.cookie in that case; see Chapter 8 for details on using cookies.
JavaScript Libraries and Frameworks
Like any language, JavaScript has a plethora of libraries and frameworks you can use in your projects. These can range from highly specific libraries designed to do one thing, to more generic libraries that enforce their own syntax, to complete frameworks for creating web-based applications in modern browsers.
Generally speaking a library is a collection of re-usable code, often small and optimized, and focused on a specific task such as providing convenience routines for complex tasks or extending the base language with new features. Common examples include mathematical libraries, libraries for accessing databases, and libraries for accessing filesystems.
In the specific case of JavaScript, libraries often extend JavaScript with new features (like animation) as well as convenience routines for more complex tasks (like asynchronous communication). In addition, JavaScript libraries can also focus on overcoming inconsistencies in the implementation of the various standards, allowing you to write code that works in multiple browsers across multiple platforms.
The term framework is a bit harder to nail down because there’s no traditional definition as there is for library. As a result, “framework” can mean many different things depending on the language and context in question. In terms of JavaScript, we like to think of frameworks as complex collections of libraries and routines that provide a predefined structure for you to fill in as you need. One way we’ve heard the difference explained is, “Libraries are things your code calls and frameworks are things that call your code.”
Choosing a Library
When it comes to choosing a library, the first step is easy: find libraries that do what you need. But many libraries have overlapping features, so how do you choose which one is best for your project? The answer isn’t very cut-and-dry, but there are some simple questions to ask yourself to help you choose:
License: Does the library have a license that is compatible with your needs? Most libraries are open source and allow redistribution, but others do not. You should check the license carefully to make sure it allows you the freedom you need for your project.
Support: What sort of support is there for the library? Is it under active development? Can you get help if you need it? Some libraries are backed by companies that sell support contracts but most are open source projects with varying degrees of documentation.
Style: Does the library work well with your programming style? How easy is it to set up and maintain in your project? Some libraries (and many frameworks) enforce their own syntax and style, and you’ll want to make sure you’re comfortable with that.
Size: How big is the library? Can the library be minified and GZipped? How expensive is the library when it loads, does it cause noticeable delays?
Security: Does the library meet your security needs? If it is an open source library, do you see anything questionable in the code for the library? Security is often ignored, but it’s as important as any of the other considerations.
Testing: Does the library have its own unit tests? How will you write tests for code that uses the library? Does that work with your existing testing practices?
Not all of these considerations will be relevant to your situation. Probably the most important are license and support, but thinking about all of them will help you make a good choice.
Here is a brief list of common JavaScript libraries and frameworks. This list isn’t meant to be conclusive or even representative, the ecosystem of JavaScript libraries is just too vast to make those claims. However, this list does represent the libraries that we have commonly encountered and worked with, and is meant as a starting point for your own exploration of JavaScript libraries.
Prototype and Scriptaculous
http://www.prototypejs.org/ and http:// script.aculo.us/
Prototype is one of the first JavaScript libraries. It provides support for classes, event delegation, AJAX convenience routines, and extensions to the DOM. Scriptaculous is built on Prototype and provides a rich framework for user interfaces and interactions. It includes support for animation effects, dragging and dropping of DOM elements, and various DOM utilities. Both libraries are well-supported by active communities and are undergoing active development. Both frameworks have their own unit tests, and Scriptaculous includes support for testing your own code.
Dojo Toolkit
http://www.dojotoolkit.org/
The Dojo Toolkit is a lightweight library that provides support for event normalization, simplified AJAX interactions, and DOM manipulation. In addition, Dojo has built-in support for modules, allowing you to encapsulate your code for reuse easily. Dojo also supports dynamic loading of modules, so that applications can load modules as they need them rather than all at once.
jQuery
http://www.jquery.org
jQuery is probably the most widely-used JavaScript library in the world. jQuery enforces a selector-based syntax, and includes features for normalizing events across platforms, simplifying AJAX interactions, basic animation, and a wide variety of convenience routines. See the next section for details on using jQuery.
Sencha ext JS
http://www.sencha.com/products/extjs/
Sencha’s ext JS is a framework focused on creating application-like user interfaces for JavaScript applications. It has a rich set of UI widgets and includes support for charting and drawing. The framework also includes an advanced layout engine that allows you to create complex interactive layouts with docking and other features.
YUI
http://www.yuilibrary.com/
YUI is a set of open source JavaScript libraries built and maintained by Yahoo. Yahoo uses YUI in their products, and as a result it is highly performant and well-tested. It includes event normalization, animation, a lightweight application framework, and several UI interactions (like drag and drop and sortables) and widgets (like a rich text editor and a datagrid). YUI has several libraries and all together is quite extensive, but you can pick and choose which libraries you need and create a customized smaller library. YUI also includes CSS libraries; we’ve had very good luck with their Fonts and CSS normalization libraries.
Closure
https://developers.google.com/closure/
Closure is Google’s JavaScript application framework. It includes the Closure JavaScript Library, the Closure Compiler, and Closure Templates. The Closure JavaScript Library includes all the standard features of a JavaScript library, as well as having a wide variety of UI interactions and widgets. Closure uses a strictly namespaced object-oriented syntax, and is probably the most accessible to developers migrating to JavaScript from languages like C#, Java, or C++. Closure also includes the Closure Compiler, which we’ve mentioned earlier in the chapter. It can compile, optimize, and minify any JavaScript, but works particularly well with JavaScript that uses the Closure Library. Finally, Closure Templates provide a templating solution for both HTML and JavaScript. The entire framework is completely unit-tested and supports (and encourages) unit testing of your own code.
Node.js
http://www.nodejs.org
Node is a server-side JavaScript platform. It’s built on Chrome’s V8 JavaScript engine and so is not only very compliant with the ECMA-262 standard but is also highly performant. Since Node operates outside of a browser context, it doesn’t have any of the usual features you would expect in a JavaScript library: it has no event normalization or DOM manipulation features. Instead, it has a set of APIs for building server features such as web servers, accessing the file system, network operations (including raw socket management), and a module system.
Montage
http://www.montagejs.org
A relative newcomer to the field, Montage is an impressive framework that is built around the latest features of JavaScript, HTML, and CSS. It is focused on building complex applications and includes its own templating system built around HTML and CSS, and has an impressive array of prebuilt UI components that work in both mobile and desktop contexts. Montage itself is built using an MVC pattern, and you will naturally fall into that pattern as you use Montage, so it’s a great introduction to MVC applications built with JavaScript. Finally, because it does a lot of the heavy lifting for you behind the scenes, Montage allows you to program pretty much in pure JavaScript without any enforced syntax like selectors, arbitrary namespaces, or other syntactic sugar.
MicroJS
http://www.microjs.com/
MicroJS is actually a clearinghouse of small JavaScript libraries that you can combine as needed. The site lets you search for libraries based on the functionality that you need. All of the libraries are quite small, but their licensing, support, style, security and testing vary, so you’ll have to explore individual libraries yourself. We mention it here because it’s a great starting point for finding libraries that are focused on a single task.
Using jQuery
Created in 2006 by John Resig, jQuery is one of the most widely used JavaScript libraries in the world. There are many libraries available, but jQuery is our preferred choice. It provides a robust and cross-browser API for everything from animation to AJAX. The biggest benefits of jQuery are:
jQuery is small. At only 32kb (minified and GZipped) it’s remarkably small for such a full-featured library.
jQuery fixes a lot of browser dependent problems. One of the main reasons jQuery was created was to help overcome the variations in browser implementations of JavaScript and the DOM standard. If you’re looking to solve cross-browser issues in your scripts, jQuery is a great candidate.
jQuery is well-tested. Not only is jQuery well-tested by its own suite of unit tests, jQuery is used on so many sites by so many people that bugs are reported quickly.
jQuery is fast. The internal coding for jQuery is highly optimized and efficient.
Writing jQuery code is fast. Because jQuery provides convenient shorthand methods for so many common tasks, writing code in jQuery can take less time than writing equivalent code in straight JavaScript.
jQuery is easy to use. All you have to do is include the jQuery script in the header of your document and all of its functionality will be available to any scripts that come after.
One of the main caveats to remember with jQuery is that because it abstracts away so much of JavaScript and the DOM that it tends to enforce its own way of doing things. That’s fine, but it’s not necessarily congruent with how you might approach the same problems in pure JavaScript. We usually recommend that novice JavaScript developers bear that in mind while they’re working with jQuery so as to avoid potentially harmful habits. (Many JavaScript libraries enforce their own syntax, so this advice applies to more than just working with jQuery.)
We’re not going to cover everything about jQuery in this section—entire books have been written about that. Fortunately, jQuery’s online documentation is some of the best available, and you can find it at http://docs.jquery.com/ You’ll find the entire jQuery API documented in detail there, along with examples for just about everything.
How It Works
jQuery works by creating a jQuery() function object in the global scope (aliased to $() for brevity; you can use either) that you can then use in your own scripts. The jQuery() function takes as a parameter a CSS selector for an element in the DOM (just like the DOM standard methods querySelector() and querySelectorAll()and returns a reference to that element wrapped in a jQuery object (similar to how JavaScript will wrap a string primitive with a String object to give it needed functionality). The resulting object is often referred to as a “jQuery selector” and it has an impressive set of methods and properties you can access that will affect the element(s) it refers to.
As an example, say you want to hide an element on the page, as in Listing 12. In regular JavaScript, you might write something like this:
Listing 12. Hiding an element using JavaScript
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
</head>
<body>
<h1>Hello World
<script>
var myHeadline = document.querySelector(“h1”);
myHeadline.style.display = “none”;
</script>
</body>
</html>
In this example, we use the querySelector() method to get a reference to the headline, and then we set an inline style property of display: none on it. As soon as the example loads in your browser, the headline will disappear (you may not even see the headline before it disappears).
In jQuery, you would provide the selector to the jQuery() function (we’ll use $() throughout these examples) and then call the hide() method on the result, as in Listing 13:
Listing 13. Hiding an element using jQuery
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”></script>
</head>
<body>
<h1>Hello World
<script>
$(“h1”).hide();
</script>
</body>
</html>
Same result as the previous example in only one line of code. Note that we have included the jQuery library in the head of our example; you must include the library before attempting to use it. jQuery is available via a content distribution network (CDN) thanks to the generosity of MediaTemple. You could just as easily download jQuery and serve your own private copy. See http://www.jquery.com/download for details and examples.
In addition to the hide() method, jQuery also provides a fadeOut() method which causes the target element to fade out of sight, as in Listing 14:
Listing 14. The jQuery fadeOut() method
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”></script>
</head>
<body>
<h1>Hello World</h1>
<script>
$(“h1”).fadeOut();
</script>
</body>
</html>
When you load this example, you’ll see the headline fade out quickly. jQuery also provides a fadeIn() method, shown in Listing 15:
Listing 15. The jQuery fadeIn() method
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”>
</head>
<body>
<h1>Hello World
<script>
$(“h1”).fadeOut();
$(“h1”).fadeIn();
</script>
</body>
</html>
In this example, we first fade out the headline, then we fade it back in. You’ll notice that we called the jQuery() function twice to select the element, which is a little inefficient. The jQuery() function is kind of expensive; it has to go and fetch the element and then wrap it in a jQuery object. If you’re going to be using the same selector more than once, you can alias it to a variable. Also, almost all jQuery methods return the original selector, so you can chain commands as in Listing 16:
Listing 16. Chaining jQuery methods for efficiency
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”></script>
</head>
<body>
<h1>Hello World
<script>
$(“h1”).fadeOut().fadeIn();
</script>
</body>
</html>
This example has exactly the same result as the previous example, but it is both more efficient (because we only call the jQuery() function once to select the element and then use chaining) and also smaller—it’s shorter by 8 characters. That’s not a big savings, but on average you’ll find jQuery code can be quite terse, resulting in smaller scripts.
Events in jQuery
jQuery started its life as a way to provide cross-browser functionality for JavaScript developers, and one of the most important ways it does this is by providing an event system that works the same in all of its supported browsers. If you’ll recall from Chapter 3, the DOM event model is not correctly implemented in Internet Explorer versions lower than 9. jQuery handles those problems for you, so that your event handlers will execute as you expect in all supported browsers.
As of version 1.7, jQuery uses the on() method to attach event handlers, and uses the off() method to remove event handlers. jQuery’s on() method has a simple syntax:
$(targetSelector).on(events, filterSelector, data, handler);
targetSelector: A selector for the element(s) you wish to attach the event handler to.
events: One or more event types; multiple event types can be separated by spaces. For example, “click keydown” would specify that the event handler should be fired whenever the user clicks on the target element, or whenever the user presses a key while the target element has keyboard focus. You can also apply namespaces to your events by appending .namespace to an event type (e.g. click.kittenRescue). This allows you to add and remove multiple event handlers of the same type to a given targetSelector.
filterSelector: a jQuery selector specifying that this event handler should only execute if the specified event types originated on elements that match filterSelector (and are children of targetSelector). This feature allows you to have a lot of flexibility in your event delegation. Note, however, that the selector is checked every time the specified events are dispatched to targetSelector. For most events that happen infrequently that isn’t a problem, but for events that fire rapidly many times (like mouseover events) it can cause a performance hit.
data: Either an object reference or an object literal. This object will be available on the resulting event object in the handler as the event.data property. This feature allows you to pass in arbitrary data into your event handlers.
handler: a function expression (which can be an anonymous inline function) that accepts an event object as a parameter, and is executed when the specified events fire on the targetSelector.
For an example using jQuery’s event system, consider our Kitten Rescue game from Chapter 3. In straight JavaScript it looks like this:
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<style>
.basket {
width: 300px;
height: 300px;
position: absolute;
top: 100px;
right: 100px;
border: 3px double #000000;
border-radius: 10px;
}
</style>
</head>
<body>
<h3>Rescue the kittens!</h3>
<p>Click on them to put them in their basket!
<ul id=“kittens”>
<li>Rowly
<li>Fred
<li>Mittens
<i>Lenore
</ul>
<ul class=“basket”>
<script>
var basket = document.querySelector(“.basket”),
kittens = document.getElementById(“kittens”);
kittens.addEventListener(“click”, function(event) {
basket.appendChild(event.target);
}, false);
</script>
</body>
</html>
Here we are delegating the click event handler to the unordered list so that we don’t have to attach an event handler to each individual list item.
Written in jQuery, the game would look like Listing 17:
Listing 17. Kitten Rescue written in jQuery
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”></script>
<style>
.basket {
width: 300px;
height: 300px;
position: absolute;
top: 100px;
right: 100px;
border: 3px double #000000;
border-radius: 10px;
}
</style>
</head>
<body>
<h3>Rescue the kittens!</h3>
<p>Click on them to put them in their basket!</p>
<ul id=“kittens”>
<i>Rowly
<li>Fred
<li>Mittens
<li>Lenore
</ul>
<ul class=“basket”></ul>
<cript>
var $basket = $(“.basket”);
$(“#kittens”).on(“click”, function(event) {
$basket.append(event.target);
});
</script>
</body>
</html>
In the jQuery version we first save a reference to the jQuery(“.basket”) selector so that we can reuse it every time the event handler fires—that way we don’t have to get a reference to the basket every time the event fires. Then we attach an even t handler to the unordered list using the on() method. We could have specified a filterSelector of “li” but in this case it was unnecessary, because events can only originate on list items.
Event handlers are removed using the off() method:
$(targetSelector).off(events, filterSelector, handler);
targetSelector: the selector specifying the element(s) to remove the event handlers from.
events: The list of event types, or namespaces, of the event handlers(s) to remove.
filterSelector: The selector specified when the event was delegated using the on() method.
handler: The function specified when the event was bound.
Note that you can specify just a namespace to remove all the event handlers that share that namespace on targetSelector. For example, if you added the following hypothetical event handlers with the same namespace:
$(targetSelector).on(“click.myEventHandler”, “li”, handleClickEvent);
$(targetSelector).on(“focus.myEventHandler”, handleFocusEvent);
You could remove both by specifying just their namespace:
$(targetSelector).off(“.myEventHandler”);
That would remove both the click event handler and the focus event handler at the same time.
A convenient event that jQuery provides is a document ready event that is triggered when the DOM of a given document has been loaded and parsed and is available for manipulation. This event gives you an extra level of flexibility in your scripts; with it you do not need to load your scripts at the end of a document (see the section on Optimization Techniques earlier in this chapter for details). You listen for the ready event on the document just like any other event on any other target element, as in Listing 18:
Listing 18. Using the document ready event in jQuery
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Developer's Guide</title>
<script src=“http://code.jquery.com/jquery-1.9.1.min.js”></script>
<script>
$(document).on(“ready”, function() {
$(“h1”).fadeOut().fadeIn();
});
</script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
In this example, we have a script in the head that accesses a DOM element. Ordinarily, the element wouldn’t be ready at the time the script was executed, so we create an event handler for the document’s ready event. When that event fires, we know the DOM is parsed and available for manipulation, so we can execute our code.
jQuery UI
jQuery UI is a library of user interface interactions and themable widgets maintained by the jQuery group. It provides a set of easy to use interactions:
Draggable: Allows you to specify elements as draggable, meaning the user can click and drag them around on the page.
Droppable: Allows you to specify elements as droppables, meaning they can have draggable elements dropped on them.
Resizable: Allows you to specify elements as resizable, meaning the user can “grab” an edge (you specify which) and resize the element.
Selectable: Allows you to select elements, multiselect using modifier keys, and marquee select by dragging and drawing a box around target elements.
Sortable: Allows you to specify a set of elements can be sorted using drag-and-drop interactions. Elements can be a simple list or a grid.
jQuery UI also has a full set of useful user interface widgets, including a datepicker and a dialog system capable of producing popups and modals. All the widgets are completely customizable in appearance. You can use the themeroller available on the jQuery UI site to roll a custom theme, or you can write custom CSS to make the widgets appear exactly as you want.
For more information about jQuery UI see http://jqueryui.com.
jQuery Mobile
jQuery Mobile provides a basic framework for producing mobile web applications. It provides a set of events focused on touch interactions like tapping and swiping, as well as interactions unique to mobile devices like orientation changes. jQuery Mobile also provides a set of mobile-optimized user interface widgets that provide UI features that are common on mobile applications, such as toolbars, expandable and collapsible content areas, sliders, and list views.
jQuery Mobile works a bit differently than other jQuery products. Instead of operating directly on jQuery selectors, jQuery Mobile defines a set of data attributes that you can apply to your markup. These data attributes provide a way for you to specify what the elements are supposed to be: headers, footers, list views, panels, buttons, sliders, etc. You create semantic markup, apply jQuery Mobile data attributes to the elements, and then load in jQuery Mobile and it initializes the elements for you. For example, consider the very basic two-page mobile application in Listing 19:
Listing 19. A two page mobile application built using jQuery Mobile
<!DOCTYPE html>
<html>
<head>
<title>jJavaScript Developer's Guide</title>
<link rel=“stylesheet” href=“http://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.css” />
<script src=“http://code.jquery.com/jquery-1.8.2.min.js”></script>
<script src=“http://code.jquery.com/mobile/1.3.0/jquery.mobile-1.3.0.min.js”></script>
</head>
<body>
<section id=“page1” data-role=“page”>
<header data-role=“header”><h1>JavaScript Developer's Guide</h1></header>
<div class=“content” data-role=“content”>
<p>Here is a sample jQuery Mobile application.</p>
<p>Go to next page</p>
</div>
<footer data-role=“footer” data-position=“fixed”><h1>Apress</h1></footer>
</section>
<section id=“page2” data-role=“page”>
<header data-role=“header”><h1>JavaScript Developer's Guide</h1></header>
<div class=“content” data-role=“content”>
<p>This is the second page.</p>
<p>
data-direction=“reverse”>Go to previous page</p>
</div>
<ooter data-role=“footer” data-position=“fixed”><h1>Apress</h1></footer>
</section>
</body>
</html>
In this example, we started with basic semantic HTML 5 markup, with sections, headers, footers, etc. Then, using jQuery Mobile’s predefined data attributes, we specified pages, headers, content areas, etc. We even specified the transitions between the pages should be a slide transition from page 1 to page 2, and a reverse slide transition when going back from page 2 to page 1.
When you load this application into a browser and jQuery Mobile initializes, it scans for data attributes, applies the necessary modifications to them, and handles all of the page management for you. jQuery Mobile exposes all of its functionality through data attributes, so it’s possible to create fairly complex mobile applications with just semantic HTML and never writing a single line of JavaScript.
This is just scratching the surface of jQuery Mobile. For more details, see jQuery Mobile’s documentation at http://jquerymobile.com.
Building a Library
As with any language, the more you work with JavaScript, the more you’ll find you are recreating the same bits of code in your projects. Maybe you have a specific way you like to enumerate objects. Maybe you have a particular way of handling DOM events. Whatever they are, it might make sense to roll these commonly-used bits into your own JavaScript library. So far in this chapter, we’ve provided a useful set of methods for performing asynchronous requests, caching data, and performing cross-domain requests.
In this chapter we’ve also introduced you to jQuery, one of the most common JavaScript libraries, so you’ve had some exposure to what a good library can do for you. What if we wanted to create our own jQuery-like library that included all of the methods we’ve built in this chapter? We would want our new library to have a selector-based syntax and to support chaining of commands, just like jQuery.
That’s actually very easy to to do. In this section we’ll create a new library called “jkl” (pronounced “Jekyll,” after the eponymous good doctor) because “jkl” is really easy to type. The basic pattern to create our library looks like Listing 20:
Listing 20. Basic library pattern
(function() {
var window = this,
undefined;
jkl = window.jkl = function(selector) {
return new jkl.jklm.init(selector);
}
jkl.jklm = jkl.prototype = {
init: function(selector) {
this.selector = selector;
return this;
}
}
jkl.jklm.init.prototype = jkl.jklm;
})();
Here we are once again using an immediately invoked function expression to create a closure that we can use as our own private playground. We want our library to exist entirely in its own namespace, and to expose only one method in the global namespace: the jkl() function. To do that, we have to do a little bit of fancy footwork:
1. We define the jkl() function in the global context (which is the window object). This function calls a constructor function called init() which lives on a subproperty of jkl. This subproperty of jkl, which we’re calling jklm (also for ease of typing) is the namespace where we’ll be adding our methods.
2. So far everything we’ve done has been fairly straightforward, but here is the first bit of mind-bending code: we set jkl.jklm to be a reference to jkl’s prototype. So any property or method we add to jkl.jklm will be added to jkl’s prototype, and will thus be available to jkl(). Another way of looking at it is that we’re creating a private namespace within jkl that will have its properties and methods exposed on jkl as if they were defined there originally.
3. Within the jkl.jklm namespace, we create our init() function, which will act as a constructor for our library. Every time someone calls jkl(selector) it will be the equivalent of calling new jkl.jklm.init(selector). Our init() function adds the selector to the newly-constructed copy of jkl, and then returns the results to the global scope.
4. Finally, we set the prototype of jkl.jklm.init to be a reference to jkl.jklm (and thus to jkl.prototype), which closes the prototype circle.
This library doesn’t do anything at the moment, because aside from init() it has no methods. We add methods by extending jkl.jklm as in Listing 21:
Listing 21. Adding a few basic methods to the library
(function() {
var window = this,
undefined;
jkl = window.jkl = function(selector) {
return new jkl.jklm.init(selector);
}
jkl.jklm = jkl.prototype = {
init: function(selector) {
this.selector = selector;
return this;
},
// Hide the target element.
hide : function() {
document.querySelector(selector).style.display = “none”;
return this;
},
// Show the target element.
show : function() {
document.querySelector(selector).style.display = “inherit”;
return this;
},
// Make the target element red
enredden : function() {
document.querySelector(this.selector).style.backgroundColor = “#F00”;
return this;
}
}
jkl.jklm.init.prototype = jkl.jklm;
})();
Now our library will have three methods: hide(), show() and enredden(). Note that at the end of every method we return this, which allows for method chaining, just like jQuery. We can use our library in Listing 22 like this:
Listing 22. Using the jkl library
<!DOCTYPE html>
<html>
<head>
<title>jJavaScript Developer's Guide</title>
<script src=“jkl-0.0.1.js”></script>
</head>
<body>
<h1>Testing the jkl Library</h1>
<script>
jkl(“h1”).enredden();
</script>
</body>
</html>
In this simple example, we use the enredden() method to change the headline’s background to red. Now, in Listing 23, we can add the other methods to the library (performing an XHR request, performing a JSONP query, and data caching):
Listing 23. Adding other methods to the jkl library
(function() {
var window = this,
undefined;
jkl = window.jkl = function(selector) {
return new jkl.jklm.init(selector);
}
jkl.jklm = jkl.prototype = {
init: function(selector) {
this.selector = selector;
return this;
},
// Hide the target element.
hide : function() {
document.querySelector(selector).style.display = “none”;
return this;
},
// Show the target element.
show : function() {
document.querySelector(selector).style.display = “inherit”;
return this;
},
// Make the target element red
enredden : function() {
document.querySelector(this.selector).style.backgroundColor = “#F00”;
return this;
},
// Perform an asynchronous request defined by myXhrDefs object.
doXHR : function(myXhrDefs) {
// Create and configure a new XMLHttpRequest object
var myXhr = new XMLHttpRequest(),
myTimer = null;
myXhr.open(myXhrDefs.strMethod, myXhrDefs.strUrl, myXhrDefs.boolAsync);
// Register the error and success handlers
myXhr.onreadystatechange = function(objEvent) {
// If readyState is 4, request is complete.
if (myXhr.readyState === 4) {
// Cancel the timeout timer if we set one.
if (myTimer !== null) {
clearTimeout(myTimer);
}
// If there’s an error, call the error callback,
// Otherwise call the success callback.
if ((myXhr.status !== 200) && (myXhr.status !== 304)) {
if (myXhrDefs.errorCallback != null) {
myXhrDefs.errorCallback(myXhr);
}
} else {
myXhrDefs.successCallback(myXhr);
}
}
}
// Handle timeouts (set myXhrDefs.intTimeout to null to skip)
// If we're working with a newer implementation, we can just set the
// timeout property and register the timeout callback.
// If not, we have to set a timer running that will execute the
// timeout callback.
if (myXhrDefs.intTimeout !== null) {
if (typeof myXhr.ontimeout !== “undefined”) {
myXhr.timeout = myXhrDefs.intTimeout;
myXhr.ontimeout = myXhrDefs.timeoutCallback;
} else {
myTimer = setTimeout(myXhrDefs.timeoutCallback, myXhrDefs.intTimeout);
}
}
// Send the request
myXhr.send(myXhrDefs.postData);
return this;
},
// Execute a cross-domain JSONP query.
executeJSONPQuery : function(strUrl) {
// Check to see if a jsonp script tag has already been injected.
// Also, create a new script tag with our new URL.
var oldScript = document.getElementById(“jsonp”),
newScript = document.createElement(“script”);
newScript.src = strUrl;
newScript.id = “jsonp”;
// If there is already a jsonp script tag in the DOM we’ll
// replace it with the new one.
// Otherwise, we'll just append the new script tag to the DOM.
if (oldScript !== null) {
document.body.replaceChild(newScript, oldScript);
} else {
document.body.appendChild(newScript);
}
return this;
},
// Perform a cached XHR request.
cachedXHR : function(myXhrDefs) {
var fetchNewData = false,
now = new Date(),
lastTimeStamp = localStorage.getItem(myXhrDefs.cacheName + “-timestamp”);
// Does the cache even have the specified item?
if (lastTimeStamp == null) {
fetchNewData = true;
} else {
// We've cached the service at least once. Check the last timestamp.
var timeStamp = new Date(lastTimeStamp);
if ((timeStamp.getTime() + (myXhrDefs.intCacheDuration * 1000)) < now.getTime()) {
fetchNewData = true;
}
}
// If we need to fetch new data, we need to extend the existing successCallback method
// to cache the new results with a new timestamp.
if (fetchNewData) {
myXhrDefs.successCallback = (function(oldCallback) {
function extendedCallback(objEvent) {
localStorage.setItem(this.cacheName + “-data”, objEvent.responseText);
localStorage.setItem(this.cacheName + “-timestamp”, now.toISOString());
oldCallback(objEvent);
}
return extendedCallback;
})(myXhrDefs.successCallback);
// Perform the XHR request.
doXHR(myXhrDefs);
} else {
// Just use the cached data.
var cachedData = localStorage.getItem(myXhrDefs.cacheName + “-data”),
fakeEvent = {
responseText : cachedData
};
myXhrDefs.successCallback(fakeEvent);
}
return this;
},
// Perform an asynchronous call to the specified URL and load the results into
// the target element.
load : function(strUrl){
var ptrTarget = document.querySelector(this.selector), myXhrDefs = {
strMethod : "GET",
strUrl : strUrl,
intTimeout: 3000,
postData: null,
boolAsync: true,
successCallback: function(objEvent) {
// Do things when the request is successful
ptrTarget.innerHTML = objEvent.responseText;
},
errorCallback: function(objEvent) {
// Do things when there is an error in the request.
console.error("The XHR failed with error ", objEvent.status);
},
timeoutCallback: function() {
// Do things when a timeout occurs.
console.error("The XHR timed out.");
}
};
this.doXHR(myXhrDefs);
return this;
}
}
jkl.jklm.init.prototype = jkl.jklm;
})();
Here we have added our doXHR(), cachedXHR(), and executeJSONPQuery() methods that we have previously defined, basically by copying and pasting their code into our library with very few modifications. The only difference is that at the end of each we return this so that we can chain methods.
In addition, we’ve added one new method: a shorthand method called load() which will perform an asynchronous call to the specified URL and place the results as the HTML in the target selector. Assuming that on our local development server we have a simple text file called ajax-test.txt that contains the text “hello world”, we can use our new load() method in Listing 24:
Listing 24. Using the new load() method
<!DOCTYPE html>
<html>
<head>
<title>jJavaScript Developer's Guide</title>
<script src="jkl-0.0.1.js"></script>
</head>
<body>
<h1>Testing The jkl Library</h1>
<script>
jkl("h1").load("http://127.0.0.1:8020/developers-guide/chapter-4/ajax-test.txt").enredden();
</script>
</body>
</html>
When you load this example, the headline will immediately be replaced with “hello world” and then its background color will be set to red. The load() method is a great example of how a library can save you a lot of trouble; it’s very common to want to fetch information from the server and insert it directly into a target element in the DOM and now our library will do that for us with a single function call. We can even make a cachedLoad() method which will use our cachedXHR() method to cache the results.
Summary
In this chapter we have tried to address the practical aspects of working with JavaScript.
We started by giving an overview of popular tools available for working with JavaScript, including IDEs and personal servers. And we discussed some basic techniques for debugging your scripts.
We discussed how to load your scripts efficiently. We also discussed the difference between perceptual delay and actual delays, and discussed techniques for dealing with both.
We talked about asynchronous communication using the XMLHttpRequest object, a technique that forms the basis of many JavaScript applications. We even built a reusable method easily employing AJAX in your own projects.
Because asynchronous communication is limited by the Same Origin Policy, we discussed some common cross-domain techniques that would allow you to bypass the policy with relative safety. As part of that process we built a simple method for efficiently performing JSONP requests.
We explored a way to cache data in the browser, for efficiency and speed, and built a function that will cache your asynchronous requests as you specify.
We discussed how to choose a JavaScript library, and reviewed some of the more common JavaScript libraries.
We did a very quick overview of jQuery and how it works. We couldn’t go into jQuery in depth, because it is a very featureful library, but we hope that we piqued your interest. We also touched briefly on jQuery UI and jQuery Mobile.
Finally, we discussed how to build your own library. By following a simple pattern, it’s easy to build your own library of methods that you use frequently.
Each of these projects provided concrete examples of many of the topics covered in this book: closures, events, prototypal inheritance, the Date object, etc.
This concludes the discussion chapters of this book. The next several chapters in the book are reference chapters, starting with a complete reference for JavaScript objects.
JavaScript in Action
Now that we’ve covered the basics of JavaScript and the DOM, let’s work with our new tools. In this chapter we have picked seven projects that will help you build your own projects, as well as illustrate many of the techniques and JavaScript features we have covered in other chapters:
Working with JavaScript
Loading Scripts Efficiently
Asynchronous Communication using XMLHttpRequest
Cross-Domain Techniques
Data Caching
Choosing a JavaScript Library
Using jQuery
Building Your Own JavaScript Library
Working with JavaScript
Although not technically a “project,” we wanted to discuss some important aspects of working with JavaScript. Probably the most common questions we get from JavaScript novices have to do with working with JavaScript: which editors are good? How do you debug? What’s the best environment to work with? Are there any tricks to working with the language? We wanted to take this opportunity to answer these questions.
Over the years, we’ve written JavaScript in just about every environment imaginable. One of the great things about JavaScript is that you don’t need a lot of tools to work with it. A simple text editor and a browser will suffice to get you started, and for basic projects that’s really all you need. Once you start working on projects with a little complexity, though, you’ll quickly find yourself wanting more advanced tools.
In this section we want to cover the basic tools of the JavaScript trade. To start we wanted to go over the trinity of JavaScript development tools: integrated development environments, browsers, and personal web servers.
We’ll start by talking a little about some of the more popular integrated development environments (IDEs) with JavaScript support. Having a solid IDE with features like syntax highlighting, code completion, refactoring support, and collaboration capabilities can help tame a complex project. There are many available, and it’s hard to know which one to pick.
We also want to cover the developer support provided by web browsers. Modern web browsers provide a wide variety of very useful tools for monitoring and debugging the JavaScript that they’re running.
Finally we’ll cover the most commonly-used personal web servers. You can use your browser’s Open File feature to test your scripts, and that’s okay for basic work. Asynchronous communication, however, is one of the cornerstones of building JavaScript applications, and it requires a web server. (We cover asynchronous communication in the Asynchronous Communication with XMLHttpRequest section, below.)
Once we’ve covered the tools of the trade we will talk about how to use them. We’ll provide some insights into our usual workflow when working with JavaScript, and then talk a little about methods for debugging your scripts.
JavaScript IDEs
Since JavaScript is essentially text, all you really need to write it is a text editor. Any text editor can serve the purpose, even something simple like the Notepad application on Windows. There are also several code editors available that work well with JavaScript, and some even provide basic features like syntax highlighting. We’ve used vi to create projects, and know several colleagues who are die-hard emacs users. We’re also very fond of TextMate and Sublime Edit, two great editing programs that support a variety of languages.
When you start working with complex projects with many JavaScript files, you’ll quickly find that you’ll need more features than simple code editors can provide. That’s where integrated development environments (IDEs) come into play. (And if you’re already familiar with code editing environments for other languages, you’ll want the same features for your JavaScript projects.)
An integrated development environment takes a code editor to the next level. A typical IDE will provide features for managing multiple files and file types, grouping them together in projects or applications (the exact term varies), and will often provide features for collaborating with other developers, such as integration with source control systems.
There are several IDEs that support JavaScript development. Typically they all provide a basic set of editing features (e.g. file creation, deletion, renaming, moving; find/replace in a single file or across multiple files; auto indenting). The better IDEs will provide more advanced features like code assist (a feature that acts as a dynamic assistant, providing suggestions or autocompletion based on what you have typed).
In addition, much of the time you won’t be working exclusively with JavaScript. If you’re working on a typical web project you’ll also be working with HTML and CSS, so you’ll want the IDE to support those as well. We find, however, that we can live with fewer features for HTML and CSS in an IDE if the JavaScript support is particularly good.
If you’re new to using IDEs entirely, we recommend looking through this list and trying a couple of the options. Many developers are passionate about their IDEs and will claim that their choice is the only logical one, but really the choice is one of personal preference. Trying a few IDEs will help you figure out what your preferences are—which features you really like, which ones you can work without—and you can make a choice at that point.
aptana studio
http://www.aptana.com
Aptana Studio is our preferred IDE. It’s built on Eclipse (see below) and is available as either a standalone install or as an Eclipse plugin. Aptana has all of the features of the base Eclipse IDE (build integration, cross-platform, scriptability, etc). In addition, Aptana has several very useful features for web development in general, and JavaScript development in particular:
Code assist with JavaScript, HTML, and CSS.
Code assist with jQuery (which has its own syntax, see Using jQuery, below).
Out-of-the-box Git integration (Git is a highly popular source control system; see http://git-scm.com/ for details).
Built-in CLI, for working with script interpreters like node (or ruby if you’re working with Rails).
JSLint integration (JSLint is a JavaScript syntax and style checker; see http://www.jslint.com/lint.html for details).
Open Source, with an active community. Bug reports are answered quickly and fixes are pushed regularly via the internal updating system.
Because it is built on Eclipse, many existing Eclipse plugins will work with Aptana (e.g. the SVN plugin).
Aptana is free.
Aptana also has the fully configurable UI of Eclipse, and also provides a nice library of predefined themes to try and change to your liking. (We’re very fond of the “Espresso Libre” theme.)
Aptana is backed by Appcelerator, a company that focuses on creating tools for building mobile applications. See http://www.appcelerator.com for details.
Eclipse
http://www.eclipse.org
Eclipse is an open-source IDE. Eclipse was built for Java development (and is itself built using Java), but now includes support for many other languages: JavaScript, C/C++, Ruby, Python, PHP, etc. Eclipse is a full-fledged IDE, so it can handle entire projects, and even has integration with popular build systems like Ant. In addition:
Eclipse is cross-platform. Because it’s built with Java, Eclipse runs on many operating systems. This means you can learn Eclipse once and not have to worry about changing from Windows to Mac and having to learn a whole new IDE.
Eclipse supports plugins. Anyone can create plugins to extend Eclipse’s functionality, and many plugins exist for everything from browser debugging to unit testing to SVN and Git integration.
Eclipse is Open Source and has an active community.
Eclipse is free.
Out of the box Eclipse provides very little support for working with JavaScript (or HTML or CSS). You can add varying degrees of support through a plugin. The most prominent Eclipse plugin for JavaScript support is Aptana (see above), but if that’s too much there are a couple other choices.
We’ve had good luck with Amateras http://amateras.sourceforge.jp/cgi-bin/fswiki_en/wiki.cgi?page=EclipseHTMLEditor which provides basic functionality including code highlighting, content assist, outlining, and validation. If all you’re looking for is basic coding support in a lightweight plugin, Amateras is a good choice.
The Eclipse Web Tools Platform is a project that aims to provide tools for all aspects of web development. You can read more at http://www.eclipse.org/webtools/. They have a JavaScript Developer’s Tools (JSDT) sub-project at http://www.eclipse.org/webtools/jsdt/ that works very well, and provides basic code editing features as well as integrated debugging, code assist with browser dependencies, code outlining and perspectives, etc. When we are working with a plain Eclipse install and cannot use Aptana, the Eclipse Web Tools Platform is our preferred plugin for JavaScript support.
Microsoft Visual Web Developer and Visual Studio Express
http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products
Microsoft’s Visual Studio is an excellent IDE, but quite expensive for a full license. To make their tools (and thus their platforms) easier to access, Microsoft has created a line of “express” versions of Visual Studio. The Visual Studio Express for Web IDE is a great environment for creating HTML/CSS/JavaScript applications. We’ve worked with the 2010 version (which was called Visual Web Developer) and it provided all the features we needed for integrating web-based UI work with .NET backend work. Features:
Integrated workflows with full versions of Visual Studio, allowing you to keep the expensive licenses to a minimum without limiting collaboration.
Code highlighting and syntax checking for JavaScript and HTML.
SVN and Git integration.
Unit testing integration.
Active community of users who are helpful about answering questions and providing suggestions.
Free.
If you will be building projects for Windows, or will be collaborating with people who do, the Visual Studio line would be a great choice.
WebStorm
http://www.jetbrains.com/webstorm
We know many JavaScript developers that swear by WebStorm and will not even consider using another IDE. Its features include:
Code highlighting and completion.
Unit testing integration.
JSlint integration.
Internal debugger.
30-day free trial, $49 for a personal license.
We’ve not used WebStorm ourselves, but we have seen enough of it over the shoulders of colleagues to be suitably impressed.
Browsers
Probably as important as your choice of IDE is your choice of browser for doing your development. You’ll be testing in all of your target browsers, of course, but you’ll have one main browser that will be your go-to choice for ongoing work, that will always be running in the background waiting for you to switch back to it and hit refresh to see your latest changes. Which browser you choose as this faithful companion will depend largely on how well it supports JavaScript development.
Not long ago, browsers had very little support for developers. You could view the source of a web page but that was it. Errors in your JavaScript (or HTML or CSS) would pass unremarked, except for inducing unpredictable behavior in your applications. Debugging a complex script was as much a matter of defensive coding and knowledge of arcane idiosyncrasies of the browser as it was a matter of following any sort of set pattern.
Today all the modern browsers (Safari, Chrome, Firefox, and Internet Explorer) have tools for supporting development. Some browsers provide highly advanced features, but all of them provide at least a JavaScript console and the ability to view generated source.
JavaScript console: They all provide a console where the browser can output error messages. The browsers also provide access to the console to your scripts (see Using the Console, below).
View generated source: All modern browsers provide a way for you to view the source of a document that results after all of the JavaScript within the page has run. If that JavaScript has modified the DOM, those changes will be reflected in the markup. This feature is enormously helpful in determining if your DOM manipulations are behaving as you expected. Each browser’s implementation of this feature is different; consult with your browser’s documentation to learn how to use it.
Most browsers have other features that are quite useful, including the ability to monitor HTTP traffic as it happens (which is helpful when you’re making asynchronous requests and are wondering if the server you queried returned the response you expected), the ability to step through code line by line, the ability to directly manipulate HTML, CSS, and even JavaScript as the application is running, etc.
In addition to native development support, some browsers (most notably Firefox) have an extensive library of third party add-ons that provide even more features. In fact, third party add-ons were among the first developer tools available. Their popularity helped convince browser manufacturers that providing native tools is a great way to attract developers to using their browsers.
Developer support features evolve constantly because modern browsers are also evolving and push regular and frequent updates. For example, Firefox recently added a 3-D view to their developer tools, which provides a three-dimensional view of the DOM which you can view from different angles.
Chrome
Google Chrome is our go-to browser for web development. The developer tools are robust and feature the ability to apply break points to JavaScript code, run stack traces, profile efficiency, and more. We work with Chrome on a daily basis and have not yet found its developer tools to be wanting.
Firefox
Firefox is probably the most extensible of the web browsers. The latest versions include an impressive set of development tools which includes the highly useful Scratchpad, which enables you to enter JavaScript code and run it in the context of the current tab.
Firefox didn’t always have such robust development tools built in, however, so for many years web developers had to rely on plugins to provide that functionality. Probably the most popular is Firebug (http://www.getfirebug.com) which provides all the features you need for web development in Firefox: a DOM inspector, script and network monitors, a JavaScript console, etc.
Another favorite Firefox extension is the Web Developer’s Toolbar (https://addons.mozilla.org/en-US/firefox/addon/web-developer/?redirectlocale=en-US&redirectslug= Web_Developer_Extension_%28external%29). This extension adds a toolbar to Firefox with many useful features: The ability to selectively enable or disable features such as scripts, image display, or Firefox’s native popup blocker; inspectors for the DOM, cookies, forms; validation tools, etc.
Internet Explorer
Internet Explorer’s development tools are a relatively new addition, but as of version 10 are quite extensive. To access Internet Explorer’s developer’s tools, hit F12 while viewing a page. This will bring up a window containing the available tools.
Internet Explorer’s tools include all of the basic ones: a console, the ability to add breakpoints to your JavaScript, efficiency profiling, network monitoring, etc. In addition, it provides several useful features such as standards validation, which submits the current page to various validators for checking. IE’s developer tools also includes an accessibility validator, which submits the current page to the validator at www.contentquality.com.
IE’s dev tools also help you manage some of Internet Explorer’s more quirky aspects. For example, using conditional comments you can write code that targets specific versions of Internet Explorer, the tools provide a way to see what mode you currently are using. You can also change modes, making it easier to test your work in different viewing modes.
(Explaining document modes in IE is a bit beyond the scope of this section; for a really good explanation see http://www.nczonline.net/blog/2010/01/19/internet-explorer-8-document-and-browser-modes/.)
Safari
Safari also has an extensive set of web developer tools. To use them go to Preferences ➤ Advanced and check the “Show Develop menu in menu bar” option. That will add a Develop item to the main menu bar, which is how you access Safari’s developer tools.
Safari’s developer tools are quite similar to Chrome’s, and that makes sense because they both share a common codebase. However, the developers tools do differ between the two browsers. Safari, for example, has the surprisingly useful Snippets editor: From the Develop menu choose “Show Snippet editor” to pop open the editor. The Snippets editor essentially provides a stripped-down browser for you to work with. Type in your HTML, CSS, and JavaScript in the top pane and it will instantly be rendered in the bottom pane. The Snippets editor is great for testing styles and prototyping interactions, giving you a place to quickly try code without having to go through the extra steps of creating a full HTML file.
Web Servers
Now that you have an editor for creating JavaScript and a browser to run it in, you need a way to get your scripts into the browser. Many of the examples in this book can be saved as simple files on your hard drive and then opened directly in the browser, but for real web development you’ll want to run your own local server for testing your work. Each operating system has its own options for web servers, and there is even one cross-platform option available.
MacOS
MacOS comes with an Apache web server built in, and prior to Mountain Lion you could activate it via the Sharing pane in the Control Panel. Apple removed the Control Panel interface, but left the server intact. You can manage the server from the command line, or we were able to find at least one person who had posted a custom Control Panel pane that purportedly restored the feature. (We didn’t try that, but if you want to give it a try head to your favorite search engine and search for “replacement system preferences pane web sharing” and you should find it right away.)
Windows
Windows has its own web server called Internet Information Services, or IIS. Most of the various editions of Windows 7 and Windows 8 do not come with IIS installed or enabled by default, but you can easily add it to your installation. We can’t cover the details here (that would be an entire chapter in and of itself), but searching for “windows 7 IIS install” or “windows 8 IIS install” should get you started.
Xampp
http://www.apachefriends.org/en/xampp.html
Xampp is an easy to install cross-platform version of the Apache web server (configured with PHP), along with the MySQL database and several other useful tools. It’s available for Windows, MacOS, Linux, and Solaris. We’ve had very good luck with Xampp on both Windows and MacOS, and recommend it.
IDE Debugging Servers
Many IDEs have built-in debugging servers. These will allow you to serve a single page, or possibly an entire project, from within the IDE. In addition to simply serving the files, the IDE will also often provide integrated features for debugging your code, like breakpoints, stack traces, and stepping through code, all in the same environment. We’ve found that the Visual Studio tools for integrated debugging are particularly good, but Aptana’s tools are also quite useful. Consult the documentation for your tool for details on how to configure and use your IDE’s integrated server.
JavaScript Development Workflow
Now that you’ve picked an editor, a browser, and a way to serve your files, you’re ready to go. At its most simple, a typical JavaScript development workflow looks like the workflow for any other language (write, test, repeat):
1. Write some code.
2. Load it in the browser and observe the results.
3. Repeat, fixing the problems that occurred or adding more features.
As you write more complex JavaScript, you’ll need ways to inspect your scripts as they execute. Some IDEs and browser developer tools provide features that help, like inspectors or breakpoints, but there are some basic techniques you can use that give you a lot of what you need.
Using the Browser Console
Throughout this book we’ve been using alerts to monitor the progress of our scripts. Alerts are fine, but they have the disadvantage of pausing the execution of a script while it waits for someone to click the OK button.
Fortunately, there is a robust alternative to alerts in the browser’s JavaScript console. All modern browsers have a JavaScript console that you can access, typically as part of the browser’s built-in Developer Tools, see Figure 1 for an example. Most browsers have a keyboard shortcut to bring up the console: for Chrome it’s Shift-Control-I, for Firefox it’s Shift-Control-J. IE also has a console; hit F12 to bring up the Developer Tools and click on the Console tab.
Figure 1. The Screenshot of the Chrome browser console
Modern browsers expose an interface for the console to JavaScript in the form of the window.console object. Each browser’s console is different, so each browser’s console object is different. But all of them provide methods for outputting your own text to the console:
console.log(strText): Output the specified text to the console as a simple log.
console.warn(strText): Output the specified text to the console as a warning.
console.error(strText): Output the specified text to the console as an error.
Both Firefox and Chrome provide ways to filter the console output so you can view just the types of output you want. Outputting text to the console has the virtue of not interrupting the execution of your script like an alert would. Now that you know about the console, you can execute any example in this book and replace the alert call with console.log and see the same output on the console, rather than as an alert.
Logging to the console provides a great way to keep an eye on the state of your scripts. One of the most common uses is to output the value of variables at different points in your script’s execution so that you can see how the variable changes. In Chrome and IE, you can output anything to the console, not just strings. If you output an object, you’ll see it enumerated on the console (see Figure 12):
Figure 12. Displaying an object in the Chrome browser console
In Chrome you can even drill down into an object to an arbitrary depth, including its prototypal inheritances. This is a great way to learn more about JavaScript and the DOM; try outputting the window object to the console and poking around in it.
In addition, you can interact directly with the console (see Figure 13). At the top of the Firefox console is an input field with a button labeled “Evaluate.” In IE’s console, it’s the input field at the bottom of the window marked with the >> symbol. In Chrome, you just click in the console window.
Figure 13. Entering JavaScript commands into the Chrome browser console
We are getting a reference to the body of the current document and then hiding it by setting its display property to none.
You can type in any valid JavaScript here and it will execute in the context of the page that’s been loaded into the browser window. As an experiment go to any page and type alert(document.title) into the console. (Usually you can just press Enter to cause the console to evaluate your code, but sometimes you have to click the Evaluate button in Firefox.) The browser window should alert the title of the page that’s loaded. If you enter window.document into the console and press enter (or evaluate), the document object should appear in the console. In Chrome, when you drill down into this object you’ll see HTML markup . . . and as you mouse over the elements Chrome will highlight the corresponding elements in the window. IE will only enumerate the first few properties on an object, and Firefox will only output the results of the toString() method of any object.
Breakpoints
One of the most useful tools for debugging scripts is the ability to set breakpoints in the code: specified points at which the browser stops executing the script and allows you to inspect the current state of the scripts and even change things. If you’ve never used breakpoints as part of your debugging process, we highly recommend giving it a try. It’s also useful for examining scripts to learn how they work.
Chrome’s developer tools support breakpoints and stepping through JavaScript code line by line. For an excellent tutorial, see https://developers.google.com/chrome-developer-tools/docs/scripts-breakpoints.
Firebug also supports breakpoints as part of script debugging. See https://getfirebug.com/wiki/index.php/Script_Debugging for details.
Loading Scripts Efficiently
As you start writing complex JavaScript applications, you’ll quickly find that one of the biggest performance issues you’ll have is slowness when your application first loads and initializes. Sometimes these problems are due to technical issues, like script blocking or inefficient download order, and sometimes these problems are purely perceptual: you can optimize your application loading process from a technical aspect, but that has side effects (like brief flashes of unmodified content) that result in your application seeming like it is slow, inefficient, or unpolished.
In this section we’ll discuss techniques that you can use that will be optimized for both actual speed and perceived speed. The first step is to talk about how browsers download and parse their assets, and then we’ll provide four common tips you can use to increase the loading efficiency of your application.
How Browsers Download and Process Content
The details of downloading and processing content aren’t specified in any standard, so browser manufacturers are free to implement any methodologies they prefer. As a result, there is some variation from browser to browser (and even from version to version of a given browser) on how that is implemented. Overall, though, there are some general commonalities:
Browsers parse HTML documents in order. As the browser parses a document, the elements it parses will become available in the DOM and it will begin downloading specified assets (images, external scripts, stylesheets, etc).
Browsers can download multiple assets in parallel. Older browsers will only download two assets in parallel, but newer browsers can handle up to six.
Loading an external JavaScript file will block other asset downloads. This is because a script might modify the DOM, or even redirect the browser to a different page, so to avoid unnecessary downloads the browser will not begin any other parallel downloads until the script is loaded, parsed, and executed.
Throughout this book our examples have illustrated document parsing order by always placing the JavaScript at the end of the HTML markup, just before the | | | |