jQuery in Action phần 6 - Pdf 20

102 CHAPTER 4
Events are where it happens!
In addition to the
bind()
command, jQuery provides a handful of shortcut
commands to establish specific event handlers. Because the syntax of each of
these commands is identical except for the method name of the command, we’ll
save some space and present them all in the following single syntax descriptor:
jQuery also provides a specialized version of the
bind()
command, named
one()
,
that establishes an event handler as a one-shot deal. Once the event handler exe-
cutes the first time, it’s automatically removed as an event handler. Its syntax is
similar to the
bind()
command and is as follows:
Command syntax: specific event binding
eventTypeName(listener)
Establishes the specified function as the event handler for the event type named by the
method’s name. The supported commands are as follows:

blur

change

click

dblclick


unload
Note that when using these shortcut methods, we cannot specify a data value to be placed
in the
event.data
property.
Parameters
listener (Function) The function that’s to be established as the event handler.
Returns
The wrapped set.
Command syntax: one
one(eventType,data,listener)
Establishes a function as the event handler for the specified event type on all elements in
the matched set. Once executed, the handler is automatically removed.
Parameters
eventType (String) Specifies the name of the event type for which the handler is to be
established.
data (Object) Caller-supplied data that’s attached to the
Event
instance for avail-
ability to the handler functions. If omitted, the handler function can be spec-
ified as the second parameter.
listener (Function) The function that’s to be established as the event handler.
Returns
The wrapped set.
The jQuery Event Model 103
These commands give us many choices to bind an event handler to matched ele-
ments. And once a handler is bound, we may eventually need to remove it.
4.2.2 Removing event handlers
Typically, once an event handler is established, it remains in effect for the remainder
of the life of the page. Particular interactions may dictate that handlers be removed

event type are to be removed.
listener (Function) If provided, identifies the specific listener that’s to be removed.
event (Event) Removes the listener that triggered the event described by this Event
instance.
Returns
The wrapped set.
104 CHAPTER 4
Events are where it happens!
originally established as a listener, it’s either defined as a top-level function (so that
it can be referred to by its top-level variable name) or a reference to it is retained by
some other means. Supplying the function as an anonymous inline reference
would make it impossible to later reference the function in a call to
unbind()
.
So far, we’ve seen that the jQuery Event Model makes it easy to establish (as
well as remove) event handlers without worries about browser differences, but
what about writing the event handlers themselves?
4.2.3 Inspecting the Event instance
When an event handler established with the
bind()
command is invoked, the
Event
instance is passed to it as the first parameter to the function. This eliminates
the need to worry about the
window.event
property under Internet Explorer, but
what about accessing the divergent properties of the
Event
instance?
Even when using jQuery to establish handlers, the

alphabetic characters, the uppercase version of the letter will be returned, regardless
of whether the user typed an uppercase or lowercase letter. For example, both a and A
will return 65. You can use shiftKey to determine which case was entered. For key-
press events, use the which property, which is reliable across browsers.
continued on next page
The jQuery Event Model 105
Importantly, the
keypress
property isn’t reliable cross-browser for non-alphabetic
characters. For instance, the left arrow key has a code of 37, which works reliably
on keyup and keydown events. Safari returns nonstandard results for these keys
on a keypress event.
We can get a reliable, case-sensitive character code in the
which
property
of keypress events. During keyup and keydown events, we can only get a case-
insensitive key code (so a and A both return 65), but we can determine case by
checking
shiftKey
.
The
Event
instance contains not only properties that give us information
regarding the event that’s handled, but also possesses a handful of methods that
lets us control the propagation of the event. Let’s dig into those.
metaKey
Set to true if the Meta key was pressed when the event was triggered, false if not.
The Meta key is the Ctrl key on PCs and the Command key on Macs.
pageX
For mouse events, specifies the horizontal coordinate of the event relative from the

4.2.4 Affecting the event propagation
In addition to standardizing the most-used properties of the
Event
instance,
jQuery provides the same benefit for the standard methods used to affect event
propagation.
The
stopPropagation()
method will prevent the event from bubbling further
up the
DOM tree (if needed, refer back to figure 4.4 for a reminder of how events
propagate), and the
preventDefault()
method will cancel any semantic action
that the event might cause. Some examples of such semantic actions are link tra-
versal for
<a>
elements, forms submissions, and toggling the state of check boxes
on a click event.
If we want to both stop the propagation of the event, as well as cancel its
default behavior, we can return
false
as the return value of the listener function.
In addition to allowing us to set up event handling in a browser-independent
manner, jQuery provides a set of commands that gives us the ability to trigger
event handlers under script control. Let’s look at those.
4.2.5 Triggering event handlers
Event handlers are designed to be invoked when their associated event triggers
the propagation of the event through the
DOM hierarchy. But there may be times

mouse event, have no value. The
target
property is set to reference the element
of the matched set to which the handler was bound.
Also because there’s no event, no event propagation takes place. The handlers
bound to the matched elements will be called, but no handlers on the ancestors of
those elements will be invoked. Remember, these commands are convenient ways
to call an event handler, not to try and emulate an event.
In addition to the
trigger()
command, jQuery provides convenience com-
mands for most of the event types. The syntax for all these commands is exactly
the same except for the command name, and that syntax is described as follows:
In addition to binding, unbinding, and triggering event handlers, jQuery offers
high-level functions that further make dealing with events on our pages as easy
as possible.
4.2.6 Other event-related commands
There are often interaction styles that are commonly applied to pages in Rich
Internet Applications and are implemented using combinations of behaviors.
Command syntax: eventName
eventName()
Invokes any event handlers established for the named event type for all matched elements.
The supported commands are as follows:

blur

click

focus


Command syntax: toggle
toggle(listenerOdd,listenerEven)
Establishes the passed functions as click event handlers on all elements of the wrapped set
that toggle between each other with every other trigger of a click event
Parameters
listenerOdd (Function) A function that serves as the click event handler for all odd-
numbered clicks (the first, the third, the fifth, and so on)
listenerEven (Function) A function that serves as the click event handler for all even-
numbered clicks (the second, the fourth, the sixth, and so on)
Returns
The wrapped set
The jQuery Event Model 109
<html>
<head>
<title>jQuery Toggle Command Example</title>
<script type="text/javascript"
src=" /scripts/jquery-1.2.1.js">
</script>
<script type="text/javascript">
$(function(){
$('#vstar').toggle(
function(event) {
$(event.target)
.css('opacity',0.4);
},
function(event) {
$(event.target)

that reduces the opacity value to 0.4 (graying out the image, a com-
mon term for indicating disablement) and an even listener that restores the opac-
ity to its full value of 1.0
d
. Because the
toggle()
command handles all the
swapping out for us, we don’t need to bother keeping track of whether the current
click is odd or even. How convenient.
All we accomplished in this example was the toggling of the image from full to
partial opacity, but it’s easy to imagine supplying listeners that would toggle any
complementary states: enabled versus disabled, for example.
Another common multi-event scenario that’s frequently employed in Rich
Internet Applications involves mousing into and out of elements.
Hovering over elements
Events that inform us when the mouse pointer has entered an area, as well as when
it has left that area, are essential to building many of the user interface elements
that are commonly presented to users on our pages. Among these element types,
the menus used as navigation systems for web applications are a common example.
A vexing behavior of the mouseover and mouseout event types often hinders
the easy creation of such elements when a mouseout event fires as the mouse is
moved over an area and its children. Consider the display in figure 4.9 (available
in the file chapter4/hover.html).
This page displays two identical (except for naming) sets of areas: an outer
area and an inner area. Load this page into your browser as you follow the rest of
this section.
Figure 4.9
This page helps
demonstrate when
mouse events fire as

we’ll see that a mouseout event has fired.
Let’s refresh the page to start over, clearing the console.
Now, move the mouse pointer into Outer 1 (noting the event), but this time
continue inward until the pointer enters Inner 1. As the mouse enters Inner 1, a
mouseout event fires for Outer 1. If we wave our pointer over the inner area, we’ll
see a flurry of mouseout and mouseover events. This is the expected behavior.
Even though the pointer is still within the bounds of Outer 1, when the pointer
enters a contained element, the event model considers the transition from the
area of Outer 1 for its contained element to be leaving the outer area.
Expected or not, we don’t always want that behavior. Often, we want to be
informed when the pointer leaves the bounds of the outer area and don’t care
whether the pointer is over a contained area or not.
We could write our handlers to detect when a mouse event is the result of leav-
ing the area or the result of merely entering a contained element, but luckily we
won’t have to. jQuery comes to our aid with the
hover()
command.
The syntax of this command is as follows: 112 CHAPTER 4
Events are where it happens!
We use the following script to establish mouse event handlers for the second set of
areas (Outer 2 and its Inner 2 child) on the hover.html example page:
$('#outer2').hover(report,report);
As with the first set of areas, the
report()

The goal for this example seems simple: to allow users to select the type and
number of appetizers they would like added to their order. No problem, right? A
series of check boxes and text boxes will do nicely as the expected
GUI element
for making multiple choices and specifying the quantities.
But there’s a small catch: for each appetizer, other options must be presented.
For example, when ordering Crab Rangoon, diners can choose sweet-and-sour
sauce, hot mustard, or (for those who can’t decide) both. Again, this shouldn’t be
a problem because we can associate a series of radio buttons representing the
options with each appetizer entry.
But as it turns out, this does lead to a small problem. With a little
HTML coding
and some
CSS magic, we create the layout shown in figure 4.10.
Even with only five appetizer choices and their corresponding options, the
number of controls is overwhelming; it may even be difficult to see the choices
the diner has made so far. The form works as required, but its usability leaves
much to be desired.
We can solve this usability dilemma by applying a principle known as progres-
sive disclosure. We don’t need to present options for an appetizer the user isn’t
ordering, so we’ll hide the radio button options until the user needs to see them.
Progressively disclosing information as it’s needed will vastly improve the
usability of the form by reducing the confusing clutter, as shown in figure 4.11.
Figure 4.10 All our appetizers and options are displayed, but the screen is a
j
umbled mess!
114 CHAPTER 4
Events are where it happens!
As a bonus, we’ll also instrument the controls so that when a quantity is entered by
the hungry user, the displayed dollar amount will reflect the price for the quantity

to hold price data
c
Holds place for
computed price
d
Contains options to be
conditionally displayed
e
Putting events (and more) to work 115
<input type="radio" name="imperial.option"
value="vegetarian"/>
Vegetarian
</label>
</div>
</div>
We repeat this HTML structure for each appetizer entry. Note that this snippet
contains no visual rendition information; such information is factored out to
CSS
definitions (which can be found in the file bamboo.css in the same folder as the
HTML file).
Similarly, note that there’s no script embedded within the
HTML markup. The
behavior of our page will be defined following the principles of Unobtrusive Java-
Script with all script properly sequestered from the
HTML markup.
We should emphasize some aspects of this structure because they will become
important when adding the behaviors to these elements. First, note that the
check box elements (as well as the radio elements further into the markup) are
contained within
<label>

e
that contains
the radio buttons representing the appetizer options. This is the element that will
be hidden until an appetizer is checked.
With the markup all settled, let’s develop the behavior of the page step by
step, starting with hiding the container element for the radio button options.
116 CHAPTER 4
Events are where it happens!
Inspecting the HTML structure of each appetizer entry allows us to concoct a
selector that matches the
<div>
elements and to use the
hide()
command on
them as follows:
$('fieldset div div').hide();
NOTE We could initially hide these elements with CSS, but doing so in script
ensures that users who turn off JavaScript (yes, there are still people who
do that) will get a usable interface, albeit at the price of some computation
load when the page is displayed. There are other reasons to do the hiding
in the ready handler that we’ll discuss in chapter 5 when we examine
wrapper methods like
hide() in greater detail.
Having tucked the appetizer options away for later display, we now turn our
attention to enabling the usability behaviors that we want the elements to exhibit.
Let’s start by tying the display of the radio button options associated with an
appetizer to whether that appetizer is checked or not.
To react to a change in the state of an appetizer check box, which should trig-
ger the change in visibility of the
<div>

.css('color',checked ? 'black' : '#f0f0f0')
.val(1)
.change()
.each(function(){ if (checked) this.focus();});
});
All that just to hide and show a
<div>
?
Well, no. Hiding and showing the options is just one of the things that we need
to do when the state of one of the check boxes changes. Let’s look at each step in
this fragment to see what it does for us.
First, the click handler stores the checked state of the check box in a variable
named
checked
. This makes for easy reference in the code, and it establishes a
local variable that we can use in any closures that we create.
Next, the handler locates the
<div>
containing the appetizer options and sets
its
CSS
display
value to hide those options when the check box is unchecked or to
show them when the check box is checked. The jQuery expression that we use
to locate the element to be shown or hidden is
$('div',$(this).parents('div:
first'))
, which equates to “find the
<div>
elements in the first ancestor element

attr()
command to set its disabled state to correspond to the
inverse of the check box state.

Apply a CSS
color
value so that the text is invisible when the control is dis-
abled. (Note that this doesn’t work on all browsers—some like Opera and
Internet Explorer don’t allow us to override the color of disabled fields.)

Set the value to 1. If we’re enabling the control, this is the default value we
want to use; when disabling the field, we want to revert to this default.

Call the change handler of the text box (which we haven’t defined yet, but
don’t worry because that’s next). This change handler will compute the
price for the appetizer and display it. Because we changed the value under
the covers (to 1), we need to call this handler to ensure that the price dis-
play is accurate.

Employ the
each()
method to obtain a reference to the element and assign
focus to that element if the check box is in checked state. Don’t you just
love closures that give us access to the
checked
local variable?
NOTE When pondering what type of event to handle for the check boxes, you
may initially have thought of capturing change events as opposed to click
events. For our scheme to work, we need to be immediately notified when
the state of a check

tent to the computed value; the expression
$('~

span:first',this)
locates the
first sibling of
this
that’s a
<span>
element. The computation is made by obtain-
ing the value of the text box and multiplying it by the value of the
price
attribute
on the parent
<span>
.
If any of these rather advanced selector expressions has you scratching your
head, it might be a good time to review the selector syntax presented in chapter 2.
Before we let the user interact with our page, we have one more thing that we
need to do. Remember how we left the
<span>
elements that are to contain the
computed values blank? Now it’s time to fill those in.
The values of the quantity text boxes were preset to 1, so all we need to do is to
perform the same computation that occurs when the values are changed. But we
don’t want to repeat any code so we trigger the change handler on the text boxes
and let that change handler do its thing.
$('span[price] input[type=text]').change();
With that, we’ve completed whipping up the appetizer order form—at least to the
point where we’ve met our stated goals. This example exposed us to some very

.val(1)
.change()
.each(function(){ if (checked) this.focus();});
});
$('span[price] input[type=text]').change(function(){
$('~ span:first',this).text(
$(this).val() *
$(this).parents("span[price]:first").attr('price')
);
});
$('span[price] input[type=text]').change();
});
</script>
</head>
<body>
<h1>Bamboo Asian Grille</h1>
<h2>Online Order Menu</h2>
<fieldset>
<legend>Appetizers</legend>
<div>
<label>
<input type="checkbox" name="appetizers"
value="imperial"/>
Fried Imperials rolls (2)
</label>
<span price="3">
<input type="text" name="imperial.quantity"
disabled="disabled" value="1"/>
$<span></span>
</span>

<label>
<input type="radio" name="spring.option"
value="shrimp"/>
Pork and Shrimp
</label>
<label>
<input type="radio" name="spring.option"
value="vegetarian"/>
Vegetarian
</label>
</div>
</div>
<div>
<label>
<input type="checkbox" name="appetizers" value="vnrolls"/>
Vietnamese rolls (4)
</label>
<span price="5">
<input type="text" name="vnrolls.quantity"
disabled="disabled" value="1"/>
$<span></span>
</span>
<div>
<label>
<input type="radio" name="vnrolls.option" value="pork"
checked="checked"/>
Pork
</label>
<label>
<input type="radio" name="vnrolls.option"

<label>
<input type="radio" name="rangoon.option" value="both"/>
Both
</label>
</div>
</div>
<div>
<label>
<input type="checkbox" name="appetizers"
value="stickers"/>
Pot stickers (6)
</label>
<span price="5">
<input type="text" name="stickers.quantity"
disabled="disabled" value="1"/>
$<span></span>
</span>
<div>
<label>
<input type="radio" name="stickers.option"
value="pork" checked="checked"/>
Putting events (and more) to work 123
Pork
</label>
<label>
<input type="radio" name="stickers.option"
value="vegetarian"/>
Vegetarian
</label>
</div>

element, it isn’t a form
at all! Complete the HTML to make a valid form that can be submitted to
the server.
124 CHAPTER 4
Events are where it happens!

Man does not live by appetizers alone! How would you go about adding new
sections for entrees, beverages, and desserts? Banana flambé sounds delight-
ful! How would these new sections affect the setup of the JavaScript code?

As diners are selecting (and deselecting) their choices, you could provide a
running total of the order amount. How would you go about keeping track
of the order total?

If the use of custom attributes is not to your liking, refactor the page to
eliminate them. But be sure that the price information remains defined in
one place only!

Perhaps the biggest flaw in the code is that it depends greatly on the posi-
tional relationships of the elements in an appetizer entry. This allowed the
markup to remain simple but at the expense of both creating a strong bind-
ing between the structure of an entry and the supporting code and introduc-
ing complex jQuery selectors. How would you go about making the code
more robust so that changes to the structure of an entry would have less
impact on the code? Adding
CSS class names to tag the elements (rather
than relying on positional relationships) would be one fine way of accom-
plishing this; how would you go about it? What other ideas do you have?
If you come up with ideas that you’re proud of, be sure to visit the Manning web
page for this book at http://www.manning.com/bibeault, which contains a link to the

For Internet Explorer 6 and 7, an
API-based proprietary event model that pro-
vides a subset of the functionality of the
DOM Level 2 Model is available.
Coding all event handling in a series of
if
statements—one clause for the
standard browsers and one for Internet Explorer—is a good way to drive our-
selves to early dementia. Luckily jQuery comes to the rescue and saves us from
that fate.
jQuery provides a general
bind()
command to establish event listeners of any
type on any element, as well as event-specific convenience commands such as
change()
and
click()
. These methods operate in a browser-independent fashion
and normalize the
Event
instance passed to the handlers with the standard prop-
erties and methods most commonly used in event listeners.
jQuery also provides the means to remove event handlers, causes them to be
invoked under script control, and even defines some higher-level commands that
make implementing common event-handling tasks as easy as possible.
We explored a few examples of using events in our pages. In the next chapter,
we’ll look at how jQuery builds upon these capabilities to put animation and ani-
mated effects to work for us.
126
Sprucing up with


Nhờ tải bản gốc
Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status