diff --git a/src/dom/event.js b/src/dom/event.js index 6801f646e..14d836724 100644 --- a/src/dom/event.js +++ b/src/dom/event.js @@ -389,8 +389,143 @@ /** * Event.observe(element, eventName, handler) -> Element + * - element (Element | String): The DOM element to observe, or its ID. + * - eventName (String): The name of the event, in all lower case, without the "on" + * prefix — e.g., "click" (not "onclick"). + * - handler (Function): The function to call when the event occurs. + * + * Registers an event handler on a DOM element. Aliased as [[Element#observe]]. + * + * `Event.observe` smooths out a variety of differences between browsers and provides + * some handy additional features as well. Key features in brief: + * * Several handlers can be registered for the same event on the same element. + * * Prototype figures out whether to use `addEventListener` (W3C standard) or + * `attachEvent` (MSIE); you don't have to worry about it. + * * The handler is passed an _extended_ [[Event]] object (even on MSIE). + * * The handler's context (`this` value) is set to the instance of the element being observed + * (even if the event actually occurred on a descendent element and bubbled up). + * * Prototype handles cleaning up the handler when leaving the page (important for MSIE memory + * leak prevention). + * * `observe` makes it possible to stop observing the event easily via [[Event.stopObserving]]. + * + * Although you can use `Event.observe` directly and there are times when that's the most + * convenient or direct way, it's more common to use its alias [[Element#observe]]. These two + * statements have the same effect: + * + * Event.observe('foo', 'click', myHandler); + * $('foo').observe('click', myHandler); + * + * The examples in this documentation use the [[Element#observe]] form. + * + *

The Handler

+ * + * Signature: + * + * function handler(event) { + * // `this` = the element being observed + * } + * + * So for example, this will turn the background of the element 'foo' blue when it's clicked: + * + * $('foo').observe('click', function(event) { + * this.setStyle({backgroundColor: 'blue'}); + * }); + * + * Note that we used `this` to refer to the element, and that we received the `event` object + * as a parameter (even on MSIE). + * + *

It's All About Timing

+ * + * One of the most common errors trying to observe events is trying to do it before the element + * exists in the DOM. Don't try to observe elements until after the + * [[document.observe dom:loaded]] event or `window` `load` event has been fired. + * + *

Preventing the Default Event Action and Bubbling

+ * + * If we want to stop the event (e.g., prevent its default action and stop it bubbling), we can + * do so with the extended event object's [[Event#stop]] method: + * + * $('foo').observe('click', function(event) { + * event.stop(); + * }); + * + *

Finding the Element Where the Event Occurred

+ * + * Since most events bubble from descendant elements up through the hierarchy until they're + * handled, we can observe an event on a container rather than individual elements within the + * container. This is sometimes called "event delegation". It's particularly handy for tables: + * + * + * + * + * + * + * + * + * + * + *
No record clicked
1First record
2Second record
3Third record
+ * + * Instead of observing each cell or row, we can simply observe the table: + * + * $('records').observe('click', function(event) { + * var clickedRow; + * clickedRow = event.findElement('tr'); + * if (clickedRow) { + * this.down('th').update("You clicked record #" + clickedRow.getAttribute("data-recnum")); + * } + * }); + * + * When any row in the table is clicked, we update the table's first header cell saying which + * record was clicked. [[Event#findElement]] finds the row that was clicked, and `this` refers + * to the table we were observing. + * + *

Stopping Observing the Event

+ * + * If we don't need to observe the event anymore, we can stop observing it with + * [[Event.stopObserving]] (aka [[Element#stopObserving]]). + * + *

Using an Instance Method as a Handler

+ * + * If we want to use an instance method as a handler, we will probably want to use + * [[Function#bind]] to set the handler's context; otherwise, the context will be lost and + * `this` won't mean what we expect it to mean within the handler function. E.g.: + * + * var MyClass = Class.create({ + * initialize: function(name, element) { + * this.name = name; + * element = $(element); + * if (element) { + * element.observe(this.handleClick.bind(this)); + * } + * }, + * handleClick: function(event) { + * alert("My name is " + this.name); + * }, + * }); + * + * Without the `bind`, when `handleClick` was triggered by the event, `this` wouldn't + * refer to the instance and so the alert wouldn't show the name. Because we used `bind`, it + * works correctly. See [[Function#bind]] for + * details. There's also [[Function#bindAsEventListener]], which is handy for certain very + * specific situations. (Normally, `bind` is all you need.) + * + *

Side Notes

+ * + * Although Prototype smooths out most of the differences between browsers, the fundamental + * behavior of a browser implementation isn't changed. For example, the timing of the `change` + * or `blur` events varies a bit from browser to browser. + * + *

Changes in 1.6.x

+ * + * Prior to Prototype 1.6, `observe` supported a fourth argument (`useCapture`), a boolean that + * indicated whether to use the browser's capturing phase or its bubbling phase. Since MSIE does + * not support the capturing phase, we removed this argument from 1.6, lest it give users the + * false impression that they can use the capturing phase in all browsers. + * + * 1.6 also introduced setting the `this` context to the element being observed, automatically + * extending the [[Event]] object, and the [[Event#findElement]] method. * - * Registers an event handler on a DOM element. **/ function observe(element, eventName, handler) { element = $(element); @@ -424,12 +559,59 @@ /** * Event.stopObserving(element[, eventName[, handler]]) -> Element + * - element (Element | String): The element to stop observing, or its ID. + * - eventName (String): _(Optional)_ The name of the event to stop observing, in all lower case, + * without the "on" — e.g., "click" (not "onclick"). + * - handler (Function): _(Optional)_ The handler to remove; must be the _exact same_ reference + * that was passed to [[Event.observe]] (see below.). * * Unregisters one or more event handlers. * * If `handler` is omitted, unregisters all event handlers on `element` * for that `eventName`. If `eventName` is also omitted, unregisters _all_ - * event handlers on `element`. + * event handlers on `element`. (In each case, only affects handlers registered via Prototype.) + * + *

Examples

+ * + * Assuming: + * + * $('foo').observe('click', myHandler); + * + * ...we can stop observing using that handler like so: + * + * $('foo').stopObserving('click', myHandler); + * + * If we want to remove _all_ 'click' handlers from 'foo', we leave off the handler argument: + * + * $('foo').stopObserving('click'); + * + * If we want to remove _all_ handlers for _all_ events from 'foo' (perhaps we're about to remove + * it from the DOM), we simply omit both the handler and the event name: + * + * $('foo').stopObserving(); + * + *

A Common Error

+ * + * When using instance methods as observers, it's common to use [[Function#bind]] on them, e.g.: + * + * $('foo').observe('click', this.handlerMethod.bind(this)); + * + * If you do that, __this will not work__ to unregister the handler: + * + * $('foo').stopObserving('click', this.handlerMethod.bind(this)); // <== WRONG + * + * [[Function#bind]] returns a _new_ function every time it's called, and so if you don't retain + * the reference you used when observing, you can't unhook that function specifically. (You can + * still unhook __all__ handlers for an event, or all handlers on the element entirely.) + * + * To do this, you need to keep a reference to the bound function: + * + * this.boundHandlerMethod = this.handlerMethod.bind(this); + * $('foo').observe('click', this.boundHandlerMethod); + * + * ...and then to remove: + * + * $('foo').stopObserving('click', this.boundHandlerMethod); // <== Right **/ function stopObserving(element, eventName, handler) { element = $(element);