TOC
- Discussion
- Simple Example
- Example Using a Closure
- More Complex Example
- Example Using an Object Instance Method
Discussion
You may be used to the "DOM0" style of hooking events:
<div id='myDiv' onclick='doSomething();'>...</div>
There's no reason you can't keep doing that when using Prototype, but you do have other options, such as using Event.observe:
<!-- In your HTML --> <div id='myDiv'>...</div>
// Somewhere in your JavaScript page init code Event.observe('myDiv', 'click', doSomething);
That tells Prototype to locate the element with the ID 'myDiv', and add a handler function 'doSomething' for the 'click' event. Note that unlike the DOM0 method, you're not coding a function call to identify the function to call, you're just giving its name — so there are no parentheses after it.
If you're familiar with addEventListener on W3C-compliant browsers or attachEvent on Internet Explorer, Event.observe is basically a sophisticated wrapper for those that handles the browser differences. (Note that even on IE, you don't use the "on" prefix with the event name — Event.observe will put it in if it's needed.)
The first parameter to Event.observe is either a string or an HTMLElement instance. If it's a string, Prototype will look for an element with that ID (as above). If it's an HTMLElement instance, Prototype will extend it if necessary and then use it directly.
This code is equivalent:
<!-- In your HTML --> <div id='myDiv'>...</div>
// Somewhere in your JavaScript page init code $('myDiv').observe('click', doSomething);
That retrieves the 'myDiv' element as an extended HTMLElement instance, then calls its observe method, which works just like Event.observe but without the initial argument (since you already have the element).
Note that the element doesn't have to have an ID; you just need to have the HTMLElement instance, which you can get any number of ways:
// Somewhere in your JavaScript page init code $$('div.clickable').each(function (div) { div.observe('click', divClicked); });
That looks for all divs with the class 'clickable' using a standard CSS selector, then loops through them using the Enumerable.each method, calling each one's observe method to hook up a handler.
In the above, we say "in your JavaScript page init code". This could be a window.onload event handler, or it could use the dom:loaded event.
Simple Example
Here's a simple example that hooks up a click event based on the ID of a div:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Event Example</title> <script type="text/javascript" src='libs/prototype.js'></script> <script type="text/javascript"> function init() { $('myDiv').observe('click', divClicked); } function divClicked() { alert('The div was clicked!'); } document.observe('dom:loaded', init); </script> </head> <body> <div id='myDiv'>Click me</div> </body> </html>
Example Using a Closure
There's no reason the handler function has to be a named function, although that's frequently best practice; it can be a closure:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Event Example</title> <script type="text/javascript" src='libs/prototype.js'></script> <script type="text/javascript"> function init() { $('myDiv').observe('click', function() { alert('The div was clicked!'); }); } document.observe('dom:loaded', init); </script> </head> <body> <div id='myDiv'>Click me</div> </body> </html>
More Complex Example
Here's a more complex example that doesn't expect that the elements to be hooked will have IDs, so long as it has some way of identifying them (in this case, because they're divs with the "clickable" class):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>More Complex Event Example</title> <style type="text/css"> .clickable { cursor: pointer; } </style> <script type="text/javascript" src='libs/prototype.js'></script> <script type="text/javascript"> function init() { $$('div.clickable').invoke('observe', 'click', divClicked); } function divClicked(evt) { var elm; var id; elm = evt.element(); id = elm.id || '(no id)'; alert('div ' + id + ' was clicked!'); } document.observe('dom:loaded', init); </script> </head> <body> <div id='div1' class='clickable'>Click me (div1)</div> <div id='div2' class='clickable'>Click me (div2)</div> <div id='div3' class='clickable'>Click me (div3)</div> <div class='clickable'>Click me (no ID)</div> <div id='div4'>But don't click me, I don't do anything</div> </body> </html>
Example Using an Object Instance Method
If you're using a method of an object instance to respond to an event, you must "bind" the handler to the object instance, since function references are just function references and have no intrinsic means of identifying which object they belong to. Here's an example using Function.bind:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>More Complex Event Example</title> <style type="text/css"> .clickable { cursor: pointer; } </style> <script type="text/javascript" src='libs/prototype.js'></script> <script type="text/javascript"> var Character = Class.create({ initialize: function(n) { this.name = n; }, clickHandler: function(evt) { alert('Hi, this is ' + this.name + '!'); }, hookElement: function(elm) { elm = $(elm); if (elm) { elm.observe('click', this.clickHandler.bind(this)); } } }); function init() { var fred; var barney; fred = new Character('Fred'); fred.hookElement('divFred'); barney = new Character('Barney'); barney.hookElement('divBarney'); } document.observe('dom:loaded', init); </script> </head> <body> <div id='divFred' class='clickable'>Click me, Fred</div> <div id='divBarney' class='clickable'>Click me, Barney</div> </body> </html>
The only reason that "this" is correct during the execution of 'clickHandler' is that we've bound it to the instance before using it as a handler. More on binding in these offsite articles: