doc: Documented Event.observe and Event.stopObserving, including vagaries about change events across browsers. [#6 state:resolved]
This commit is contained in:
parent
c57c2d270c
commit
41d1088160
186
src/dom/event.js
186
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.
|
||||
*
|
||||
* <h4>The Handler</h4>
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* <h4>It's All About Timing</h4>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* <h4>Preventing the Default Event Action and Bubbling</h4>
|
||||
*
|
||||
* 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();
|
||||
* });
|
||||
*
|
||||
* <h4>Finding the Element Where the Event Occurred</h4>
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* <table id='records'>
|
||||
* <thead>
|
||||
* <tr><th colspan='2'>No record clicked</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr data-recnum='1'><td>1</td><td>First record</td></tr>
|
||||
* <tr data-recnum='2'><td>2</td><td>Second record</td></tr>
|
||||
* <tr data-recnum='3'><td>3</td><td>Third record</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* <h4>Stopping Observing the Event</h4>
|
||||
*
|
||||
* If we don't need to observe the event anymore, we can stop observing it with
|
||||
* [[Event.stopObserving]] (aka [[Element#stopObserving]]).
|
||||
*
|
||||
* <h4>Using an Instance Method as a Handler</h4>
|
||||
*
|
||||
* 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.)
|
||||
*
|
||||
* <h4>Side Notes</h4>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* <h4>Changes in 1.6.x</h4>
|
||||
*
|
||||
* 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.)
|
||||
*
|
||||
* <h4>Examples</h4>
|
||||
*
|
||||
* 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();
|
||||
*
|
||||
* <h4>A Common Error</h4>
|
||||
*
|
||||
* 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);
|
||||
|
Loading…
Reference in New Issue
Block a user