Welcome to Ajax apps—it’s a whole new web world.
So you’ve built your first Ajax app, and you’re already thinking about how to change all your web apps to make requests asynchronously. But that’s not all there is to Ajax programming. You’ve got to think about your applications differently. Just because you’re making asynchronous requests, doesn’t mean your application is user-friendly. It’s up to you to help your users avoid making mistakes, and that means rethinking your entire application’s design.
Note from HR: Can we use a less offensive term? How about “consistently annoys every one of Mike’s users”?
Mike’s got the hippest movie reviews going, and he’s taking his popular opinions online. Unfortunately, he’s having problems with his registration page. Users visit his site, select a username, type in a few other details, and submit their information to get access to the review site.
The problem is that if the username’s taken, the server responds with the initial page again, an error message... and none of the information the user already entered. Worse, users are annoyed that after waiting for a new page, they get nothing back but an error message. They want movie reviews!
Ajax is exactly the tool you need to solve the problem with Mike’s page. Right now the biggest problem is that users have to wait for a full page refresh to find out their requested username is already taken. Even worse, if they need to select a different username, they’ve got to re-type all their other information again. We can fix both of those problems using Ajax.
We’ll still need to talk to the server to find out whether a username has been taken, but why wait until users finish filling out the entire form? As soon as they enter a username, we can send an asynchronous request to the server, check the username, and report any problems directly on the page—all without any page reloads, and without losing the user’s other details.
It’s okay if you didn’t think about sending the request as soon as the user types in their username... but bonus credit if you did!
Don’t annoy your users... ever!
On the Internet, your competitors are only a click away. If you don’t tell your users about a problem right away, or if you ever make them re-do something, you’re probably going to lose them forever.
Mike’s site may not be a big moneymaker (yet), or even seem that important to you... but it might to his fans. One day a user you’re helping him not annoy may land him a six-figure income writing movie reviews for the New York Times. But Mike won’t ever know if his site is hacking his users off. That’s where your Ajax skills can help.
The basic structure of Mike’s registration page is already in place, so let’s go ahead and add a <script>
tag to load the JavaScript we’ll write. Then, we can set up the username field on the web form to call a JavaScript function to make a request to the server.
Use an opening and closing <script> tag.
Some browsers will error out if you use a self-closing <script> tag, like <script />. Always use separate opening and closing tags for <script>.
Download the registration page’s XHTML and CSS.
If you haven’t already done so, download the sample files for the chapter from www.headfirstlabs.com
. Look in the Chapter2 folder for the file named registration.html
, and then add the script tag shown in bold.
Separate your page’s content from its behavior.
We could call the JavaScript directly from the XHTML by, for example, putting an onblur
event in the username form field. But that’s mixing the content of our page with its behavior.
The XHTML describes the content and structure of the page: what data is on the page, like the user’s name and a description of the movie review site, and how it’s organized. But how a page reacts to the user doing something is that page’s behavior. That’s usually where your JavaScript comes in. And the CSS defines the presentation of your page: how it looks.
Keeping content, behavior, and presentation separate is a good idea, even when you’re building a relatively simple page all by yourself. And when you’re working on complex applications that involve a lot of people, it’s one of the best ways to avoid accidentally messing up somebody else’s work.
We want some JavaScript code to run when the registration page loads, and that means attaching that code as the event handler on one of the first page events, window.onload
.
And we can do that programmatically by setting the onload
property of the window
object. But how do we do that? Let’s look at exactly what happens when the registration page is requested by a user visiting Mike’s movie review site:
First, a user points their browser at Mike’s registration page.
Then, the browser starts parsing the page, asking for other files as they’re referenced.
If the file is a script, the browser parses the script, creates objects, and executes any statements not in a function.
Finally, after all referenced files are loaded and parsed, the browser triggers the window.onload event and calls any function that’s registered to handle that event.
We want to set an event handler up to run as soon as a user loads the registration page. So we need to assign a function to the onload
property of the window
object.
And to make sure this event handler is assigned as soon as the page loads, we just put the assignment code outside of any functions in validation.js
. That way, before users can do anything on the page, the assignment happens.
There’s a lot going on in this step. Let’s go through it to make sure everything’s happening exactly when we want it to.
First...
When the browser loads the XHTML file, the <script>
tag tells it to load a JavaScript file. Any code that’s outside of a function in that script file will be executed immediately, and the browser’s JavaScript interpreter will create the functions, although the code inside those functions won’t run yet.
...and then...
The window.onload
statement isn’t in a function, so it will be executed as soon as the browser loads the validation.js
script file.
The window.onload
statement assigns the initPage()
function as an event handler. That function will be called as soon as all the files the XHTML refers to have been loaded but before users can use the web page.
Even though these happen in sequence, ALL of this occurs before users can interact with the web page.
...and finally...
The initPage()
function runs. It finds the field with an id of “username.” Then, it assigns the checkUsername()
function to the onblur
event of that field.
This is the same as putting onblur="checkUsername()"
in the XHTML. But our way is cleaner because it separates the code (the JavaScript function) from the structure and content (the XHTML).
Before we can test out all our work on Mike’s registration page, we need to check out the server. What does the server need to get from our request? What can we expect from the server?
We’ve already used window.onload
and an initPage()
function twice: once for Rob’s rock and roll store, and again for Mike’s registration page. Next up is creating a request object that works the same for the registration page as it did for Rob’s rock and roll site.
In fact, lots of things in Ajax apps are the same. Part of your job, though, is to build code so you don’t have to write those same bits of code over and over again. Let’s see how creating and using a request object looks in Mike’s movie review site:
We need a function to create a request object in almost every Ajax application... and we’ve already got one. It’s the createRequest()
function you saw back in Chapter 1, in fact. Let’s take a closer look at how this function creates a request in all types of situations, with all types of client browsers.
Copy and paste is not good code reuse.
The createRequest()
function for Mike’s movie site is identical to the createRequest()
function from Rob’s site in Chapter 1. And copying that code from the script you wrote in Chapter 1 into your new validation.js is a bad idea. If you need to make a change, you’ll now have to make it in two places. And what do you think will happen when you’ve got ten or twenty Ajax apps floating around?
When you find code that’s common across your apps, take that code out of application-specific scripts, and put it into a reusable utility script. So for createRequest()
, we can pull it out of validation.js
in the movie site and create a new script. Let’s call it utils.js
and start putting anything that’s common to our apps into it.
Then, each new app we write can reference utils.js,
as well as a script for application-specific JavaScript.
It’s time to break into JavaScript and figure out exactly what’s going on. Let’s walk through exactly what each piece of createRequest()
does, step by step.
Create the function
Start by building a function that any other code can call when it needs a request object.
Try to create an XMLHttpRequest for non-Microsoft browsers
Define a variable called request, and try to assign to it a new instance of the XMLHttpRequest
object type. This will work on almost all browsers except Microsoft Internet Explorer.
Try to create an ActiveXObject for Microsoft browsers
In the catch block, we try to create a request object using the syntax that’s specific to Microsoft browsers. But there are two different versions of the Microsoft object libraries, so we’ll have to try both of them.
If all else fails, return null
We’ve tried three different ways of obtaining a request object. If the parser reaches this request block, that means they’ve all failed. So declare request
as null, and then let the calling code decide what to do about it. Remember, null
is the object you have when you don’t have an object.
Put it together, and return request
All that’s left is to return request
. If things went okay, request
points to a request object. Otherwise, it points to null
:
Even though there was already a web form for Mike’s registration page, we’ve got to interact with that form to get the user’s username, and later on, to update the page with an error message if the selected username’s taken.
And even though we’re letting someone else worry about writing the server-side code, we’ve still got to know what to send to that code... and how to send that information.
Take a look at the steps we need to perform to check a username for validity. Most of these steps are about interacting with either the web form or a server-side program:
Try to get a request object
Show an alert if the browser can’t create the request
Get the username the user typed into the form
Make sure the username doesn’t contain problematic characters for an HTTP request
Append the username to server url
Tell the browser what function to call when the server responds to the request
Tell the browser how to send the request to the server
Send the request object
Good Ajax design is mostly about interactions. You’ve got to interact with your users via a web page, and your business logic via server-side programs.
Now we’re just about ready to actually have the server respond to our request:
Asynchronous apps behave differently than traditional web apps, and your debugging has to account for that.
Asynchronous applications don’t make you wait for a server’s reply, and you don’t get an entire page back from the server. In fact, most of the interactions between a web page and a server in asynchronous apps are completely invisible to a user. If the user’s web browser runs into a problem when it executes some JavaScript, most of the time it will just stop, and you’ll have no idea what happened.
Alerts are a good way to track down problems the browser doesn’t tell you about. Alerts show you what the browser sees. They let you know what’s going on in the background while your users are happily typing away.
You can’t usually rely on a server to tell you there’s a problem in asynchronous apps. It’s YOUR job to figure out if there’s a problem, and respond to it in a useful manner.
All we have left to do is write the code that the browser will call when the server responds to the request. That’s where the request object comes into play. It lets us tell the browser what to do, and we can use it to ask the browser to make a request to the server and give us the result.
But how does that actually happen? Remember, the request object is just an ordinary JavaScript object. So it can have properties, and those properties can have values. There are several that are pretty useful. Which do you think we’ll need in our callback function?
The browser makes the server’s response available to your code through the properties of the request object.
Although it’s easy to talk about your code “sending a request object to the server,” that’s not exactly what happens. In fact, you talk to the web browser, not the server, and the browser talks to the server. The browser sends your request object to the server, and the browser translates the server’s response before giving that response data back to your web page.
Everything works. But when you give all your code to Mike, and he goes live with the new improved registration page, there are still some problems:
What happened? Did all the work you put into the registration page get lost? Ignored?
What do YOU think?
Suppose a user does just what you expect: they enter a username, and while an asynchronous request is going to the server and getting handled by the browser, your callback is running, and the user’s filling out other information on the form. Everything works great—just like you planned.
But suppose the user’s so eager to get to Mike’s review of Iron Man that they put in their username, ignore everything else on the form, and click “Register.” What happens then?
Frank: Well, we can’t keep users from skipping over fields, but maybe we can keep them from getting ahead of our request.
Jill: You mean validating the username? Yeah, that’s perfect, but how do we do that?
Frank: How about we just disable the Register button until the server responds to the username validation request.
Jill: That would solve this problem, but it seems like we need something more.
Frank: Like what? They’re submitting the form too soon, so if we prevent the submission, the problem’s solved.
Jill: Well, don’t you think we need to give the user some idea about what’s going on?
Frank: They’ll know what’s going on when we enable the button. Until then, they should be filling out the form, not trying to click ‘Register.’
Jill: But don’t you think that might be confusing? If the user finishes filling out the form, or doesn’t want to fill it all out, then they’re just going to be sitting there, stuck, and they won’t know why.
Frank: Well, we need to let them know the application is doing something. What about displaying a message?
Jill: Another alert? That’s just going to annoy them in a different way. How about a graphic? We could display an image when we send the request to the browser...
Frank: ...and another when their username’s verified.
Jill: Hey, and if we used an image to show whether the username is okay or not, we could get rid of the alert when there’s a problem with the username, too.
Frank: Perfect! Visual feedback without annoying popups. I love it!
You can never assume your users will do things exactly the way you do... plan for EVERYTHING!
What do you think about this approach? Does it follow the principle of separating content from presentation? Would you change anything?
Try and keep your presentation in your CSS, and your behavior in your JavaScript.
Your XHTML stores structure and content. Your CSS should handle presentation, like images, colors, and font styles. And your JavaScript should be about what your page does: the page’s behavior. Mixing those means that a designer won’t be able to change an image because it’s in your code. Or a programmer will have to mess with a page author’s structure. That’s never a good thing.
It’s not always possible, but when you can, keep your presentation in your CSS, and use JavaScript to interact with the CSS rather than affecting the presentation of a page directly.
Instead of changing an image directly, let’s put all the image details in our CSS. Open up movies.css
and add the following CSS selectors:
Now our JavaScript doesn’t need to know any image names, paths, or anything about how the process icons are being shown. Instead, we just need to know the three CSS classes that represent each stage of processing.
Now we can update our JavaScript (again). This time we’ll just change the CSS class instead of directly changing an image:
Mike’s web designer made lots of changes... but she didn’t change the names of the CSS classes for each stage of processing. That means that all your JavaScript still works, with no updates! When you separate your content from your presentation, and separate both from your behavior, your web application gets a lot easier to change.
In fact, the CSS can change anytime, and we don’t even need to know about it. As long as those CSS class names stay the same, our code will happily keep on working.
Good separation of content, presentation, and behavior makes your application a lot more flexible.
With process indicators in place, all that’s left is to disable the Register button when the page loads, and then enable the button once a username’s okay.
That involves just a few more changes to validation.js
:
Disable the Register button | |
When a user first loads the page, the username hasn’t been checked. So we can disable the Register button right away in our initialization code. |
Enable the Register button | |
If the username is okay, the user’s ready to register, so we need to enable the Register button. But if there’s a problem with the username, they need to try again, so we should keep the Register button disabled. And just to make things easier for the user, let’s move them back to the username field if their username is rejected: |
The image files referenced in your CSS are in the download folder from Head First Labs.
Be sure you’ve got the complete examples folder from Head First Labs, including the process indicator image.
Now Mike’s page...
...lets users keep working while their requested usernames are verified by Mike’s server.
...prevents user mistakes by disabling buttons that aren’t safe or appropriate to use, and enables those buttons when they are useful.
...doesn’t annoy his users with intrusive popups, but still gives them useful visual feedback.
3.145.193.81