<html>
<head>
<title>Demonstrating how the event object is passed to handlers</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Click me!
</div>
<script type="text/javascript">
function handleFooClick(e)
{
// you can now do something with e,
// such as call Event.element(e)
}
$('foo').observe('click', handleFooClick);
</script>
</body>
</html>
Stopping an Event
Frequently you will want to stop an event from completing, since you
are defining a method by which to handle the event, and therefore don't
want the browser to use its own handling method.
The two best examples of this are for hyperlinks and forms. Firstly,
let's look at links. Often you will want to perform some action when
the user clicks a link, however you don't want the browser to follow
the link.
Note: This is especially useful if you want to
use Ajax to retrieve the page at the given link. This specific example
will be shown in the fifth article of this series.
Before using Prototype, you might be more familiar with returning false from the event handler. For example, you might use <a href="..." onclick="doSomething(); return false">...</a>. By returning false in the onclick handler, the browser knows not to follow the link in the href attribute.
In order to achieve this same effect with Prototype, the Event.stop() method is used instead. Returning false from your handler method will have no effect. Calling Event.stop() tells Prototype to stop event propagation and not perform the default browser action.
Note: If you have multiple handlers for a given event, all of them will be executed regardless of whether you have called Event.stop() in any or all the handlers. I'll cover this in more detail shortly.
Listing 6 shows an example of stopping an event if required to do
so. In this example, a JavaScript confirmation box is displayed to the
user. If they click OK, then the link is followed, whereas clicking Cancel will result in the link not being followed.
Note: Although not specifically related to this
concept, you should typically use a POST form if your action has some
side-effect on your application (such as deleting data from a database)
rather than a normal hyperlink.
Listing 6 Stopping an event with Event.stop() (listing-6.html)
<html>
<head>
<title>Stopping an event with Event.stop()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<a href="/path/to/do/something.php" id="myLink">Do Something!</a>
</div>
<script type="text/javascript">
function onMyLinkClick(e)
{
var msg = 'Are you sure you want to do this?';
if (confirm(msg)) {
// user click ok, nothing to do - link will be followed as normal
}
else {
// user clicked cancel, stop the event
Event.stop(e);
// link will now not be followed
}
}
$('myLink').observe('click', onMyLinkClick);
</script>
</body>
</html>
This same concept can be useful implementing JavaScript-based form validation. By observing the submit event, you can then check form values before deciding whether or not to allow the browser to submit the form.
Listing 7 shows an example of how this is achieved. In this example,
I've created a form with a single text input. The validation routine (onFormSubmit()) checks if this text input is blank (we covered the blank() method in the third article of this series), and if so records an error.
To complete the event handler, we check if there are any errors (by checking the size of the errors array), and if so we stop the event from propagating and display an error message.
Note: The serialize() method is a special method for forms that allows you to easily retrieve all of the values in a single object. If true is not passed as the first argument then the data is returned as a "get" style string (which is difficult for us to validate).
Listing 7 Validating a form before deciding whether it should be submitted (listing-7.html)
<html>
<head>
<title>Validating a form before deciding whether it should be submitted</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<form method="post" action="/some/form/target.php" id="theForm">
<div>
<input type="text" name="name" />
<input type="submit" />
</div>
</form>
</div>
<script type="text/javascript">
function onFormSubmit(e)
{
// retrieve all values to check
var values = $('theForm').serialize(true);
// placeholder array for errors
var errors = [];
// check if the name was entered
if (values.name.blank()) {
errors.push('Please enter the name');
}
// check if any errors were found
if (errors.size() > 0) {
// display the errors
alert('There were errors:\n' + errors.join('\n'));
// prevent the form from being submitted
Event.stop(e);
}
}
$('theForm').observe('submit', onFormSubmit);
</script>
</body>
</html>
Note: Even though form validation using
JavaScript is very useful to the user, you should not be relying solely
on this validation in your application. You still need to validate all
values on the server-side, since it is trivial for a user to bypass the
JavaScript validation.
Technically you could observe the click event on the submit button rather than the submit
event on the form, however the form might be submitted using a method
other than this button. For instance, if the user presses enter while
filling out the text input the form will be submitted (triggering the submit event but not the button click event).
Checking for Stopped Events
As mentioned in the previous section, if you have multiple event
handlers, each of them will still be executed even if you called Event.stop() at any time. You can, however, check to see if an earlier handler has already stopped the event by reading the stopped property.
Listing 8 shows an example of doing this. In it, I have defined two different handlers for the click
event, each of which will display an alert box. However, the alert box
will only be shown if the event hasn't already been stopped.
Listing 8 Checking for stopped events (listing-8.html)
<html>
<head>
<title>Checking for stopped events</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Click here
</div>
<script type="text/javascript">
function handler1(e)
{
if (!e.stopped) {
alert('Handler 1');
Event.stop(e);
}
}
function handler2(e)
{
if (!e.stopped) {
alert('Handler 2');
Event.stop(e);
}
}
$('foo').observe('click', handler1);
$('foo').observe('click', handler2);
</script>
</body>
</html>
Finding the Element on Which an Event Was Observed
In all of the event handlers in the examples so far, we have only
observed events on a single element. In the event handler we have then
used the $() function to select that element again to perform some function on it.
Using the Event.element() function, we can determine
exactly which element and event was triggered on. This is useful when
an event handler might be used for a particular event that may occur on
several items.
Listing 9 shows such an example. In this listing, we use the $$() function select all of the list items so the click event can be observed on each of them. Using the each() method (covered in the third article of this series), we loop over each item and observe the event.
When the event is handled (that is, the onItemClick() method is called), we use Event.element(e) to determine the specific list item that the event was triggered for. We then update its background colour and change its text.
Listing 9 Retrieving an event's element with Event.element() (listing-9.html)
<html>
<head>
<title>Retrieving an event's element with Event.element()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul id="foo">
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
</ul>
</div>
<script type="text/javascript">
function onItemClick(e)
{
var item = Event.element(e);
item.setStyle({ backgroundColor : '#fc0' });
item.update('Clicked!');
}
$$('#foo li').each(function(item) {
item.observe('click', onItemClick);
});
</script>
</body>
</html>
Sometimes you may not want the specific element on which the event
occurred, but rather one of its parent elements. If you look at listing
9, how would we go about retrieving the parent <ul> element rather than the <li> that was clicked?
To do so, Prototype provides the method Event.findElement(). This is similar to Event.element(), except you specify a tag name as the second argument. That, you would use Event.findElement('e', 'ul') to find the parent <ul> element. This is shown in Listing 10.
Listing 10 Finding an event element's ancestor with Event.findElement() (listing-10.html)
<html>
<head>
<title>Finding an event element's ancestor with findElement()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<ul id="foo">
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
</ul>
</div>
<script type="text/javascript">
function onItemClick(e)
{
var list = Event.findElement(e, 'ul');
list.setStyle({ border : '2px solid #fc0'});
}
$$('#foo li').each(function(item) {
item.observe('click', onItemClick);
});
</script>
</body>
</html>
The only problem with Event.findElement() though is
that you can only find ancestor elements based on their tag name. If
you wanted a more complex search (such as finding an ancestor with a
particular class name), then you would want to combine Event.element() with the up() method (we covered up() in the first article of this series).
Listing 11 shows an example of doing this. In this example we find the div with the class name of outer. If we tried to use Event.findElement(), we would not have been able to retrieve this element since if we passed div as the second argument, the div with class inner would have been returned.
Listing 11 Combining Event.element() with up() (listing-11.html)
<html>
<head>
<title>Combining Event.element() with up()</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div class="outer">
<div class="inner">
<ul id="foo">
<li>Click Me</li>
<li>Click Me</li>
<li>Click Me</li>
</ul>
</div>
</div>
<script type="text/javascript">
function onItemClick(e)
{
var item = Event.element(e);
var outer = item.up('div.outer');
outer.setStyle({ backgroundColor : '#cf9' });
}
$$('#foo li').each(function(item) {
item.observe('click', onItemClick);
});
</script>
</body>
</html>
Handling Keyboard Events
So far we haven't concerned ourselves with any specifics of an event
that occurs, other than on which element the event occurred. Often an
event will be triggered by a particular key press or by some movement
or action with the mouse. In this section we will look at how to handle
events that are triggered by the keyboard.
To determine which key was pressed, you can read the keyCode
property of the event object passed to your event handler. Prototype
defines a number of useful constants that help you determine which key
was pressed. Specifically, these are:
- Event.KEY_BACKSPACE
- Event.KEY_TAB
- Event.KEY_RETURN
- Event.KEY_ESC
- Event.KEY_LEFT
- Event.KEY_UP
- Event.KEY_RIGHT
- Event.KEY_DOWN
- Event.KEY_DELETE
- Event.KEY_HOME
- Event.KEY_END
- Event.KEY_PAGEUP
- Event.KEY_PAGEDOWN
- Event.KEY_INSERT
To demonstrate how these codes can be used, I have written a simple
example, shown in Listing 12. In this example there is a text input. If
you hit the escape key the text in the input is selected, while hitting
backspace results in the entire value being cleared. While this doesn't
serve much practical use, hopefully it demonstrates how you can read
the key codes.
Listing 12 Determining which key was pressed (listing-12.html)
<html>
<head>
<title>Determining which key was pressed</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="text" id="foo" />
</div>
<script type="text/javascript">
function onFooKeyup(e)
{
var element = Event.element(e);
switch (e.keyCode) {
case Event.KEY_ESC:
element.activate();
Event.stop(e);
break;
case Event.KEY_BACKSPACE:
element.value = '';
Event.stop(e);
break;
}
}
$('foo').observe('keyup', onFooKeyup);
</script>
</body>
</html>
Handling Mouse Events
It is possible to return the coordinates of the mouse for a particular event using the Event.pointerX() and Event.pointerY()
functions. You simply pass in the event object passed to the event
handler as the first and only argument to receive the integer value
representing the location of the mouse.
These methods return values relative to the entire page, not just
what is currently visible to you. In other words, if you've scrolled
down on the page the returned values are still relative to the very top
of the page.
Listing 13 shows an example of tracking the mouse movement within a particular element using the mousemove
event. This event is triggered every time the mouse is moved whilst
over this element. In the handler function we read the X and Y position
of the mouse and update the element to display this information.
Listing 13 Reading the X and Y coordinates of the mouse (listing-13.html)
<html>
<head>
<title>Reading the X and Y coordinates of the mouse</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div id="foo">
Move the mouse over me
</div>
<script type="text/javascript">
function onMouseMove(e)
{
var element = Event.element(e);
element.update(Event.pointerX(e) + 'x' + Event.pointerY(e));
}
$('foo').observe('mousemove', onMouseMove);
</script>
</body