|
602
|
1 |
YUI.add('event-valuechange', function (Y, NAME) { |
|
|
2 |
|
|
|
3 |
/** |
|
|
4 |
Adds a synthetic `valuechange` event that fires when the `value` property of an |
|
|
5 |
`<input>`, `<textarea>`, `<select>`, or `[contenteditable="true"]` node changes |
|
|
6 |
as a result of a keystroke, mouse operation, or input method editor (IME) |
|
|
7 |
input event. |
|
|
8 |
|
|
|
9 |
Usage: |
|
|
10 |
|
|
|
11 |
YUI().use('event-valuechange', function (Y) { |
|
|
12 |
Y.one('#my-input').on('valuechange', function (e) { |
|
|
13 |
Y.log('previous value: ' + e.prevVal); |
|
|
14 |
Y.log('new value: ' + e.newVal); |
|
|
15 |
}); |
|
|
16 |
}); |
|
|
17 |
|
|
|
18 |
@module event-valuechange |
|
|
19 |
**/ |
|
|
20 |
|
|
|
21 |
/** |
|
|
22 |
Provides the implementation for the synthetic `valuechange` event. This class |
|
|
23 |
isn't meant to be used directly, but is public to make monkeypatching possible. |
|
|
24 |
|
|
|
25 |
Usage: |
|
|
26 |
|
|
|
27 |
YUI().use('event-valuechange', function (Y) { |
|
|
28 |
Y.one('#my-input').on('valuechange', function (e) { |
|
|
29 |
Y.log('previous value: ' + e.prevVal); |
|
|
30 |
Y.log('new value: ' + e.newVal); |
|
|
31 |
}); |
|
|
32 |
}); |
|
|
33 |
|
|
|
34 |
@class ValueChange |
|
|
35 |
@static |
|
|
36 |
*/ |
|
|
37 |
|
|
|
38 |
var DATA_KEY = '_valuechange', |
|
|
39 |
VALUE = 'value', |
|
|
40 |
NODE_NAME = 'nodeName', |
|
|
41 |
|
|
|
42 |
config, // defined at the end of this file |
|
|
43 |
|
|
|
44 |
// Just a simple namespace to make methods overridable. |
|
|
45 |
VC = { |
|
|
46 |
// -- Static Constants ----------------------------------------------------- |
|
|
47 |
|
|
|
48 |
/** |
|
|
49 |
Interval (in milliseconds) at which to poll for changes to the value of an |
|
|
50 |
element with one or more `valuechange` subscribers when the user is likely |
|
|
51 |
to be interacting with it. |
|
|
52 |
|
|
|
53 |
@property POLL_INTERVAL |
|
|
54 |
@type Number |
|
|
55 |
@default 50 |
|
|
56 |
@static |
|
|
57 |
**/ |
|
|
58 |
POLL_INTERVAL: 50, |
|
|
59 |
|
|
|
60 |
/** |
|
|
61 |
Timeout (in milliseconds) after which to stop polling when there hasn't been |
|
|
62 |
any new activity (keypresses, mouse clicks, etc.) on an element. |
|
|
63 |
|
|
|
64 |
@property TIMEOUT |
|
|
65 |
@type Number |
|
|
66 |
@default 10000 |
|
|
67 |
@static |
|
|
68 |
**/ |
|
|
69 |
TIMEOUT: 10000, |
|
|
70 |
|
|
|
71 |
// -- Protected Static Methods --------------------------------------------- |
|
|
72 |
|
|
|
73 |
/** |
|
|
74 |
Called at an interval to poll for changes to the value of the specified |
|
|
75 |
node. |
|
|
76 |
|
|
|
77 |
@method _poll |
|
|
78 |
@param {Node} node Node to poll. |
|
|
79 |
|
|
|
80 |
@param {Object} options Options object. |
|
|
81 |
@param {EventFacade} [options.e] Event facade of the event that |
|
|
82 |
initiated the polling. |
|
|
83 |
|
|
|
84 |
@protected |
|
|
85 |
@static |
|
|
86 |
**/ |
|
|
87 |
_poll: function (node, options) { |
|
|
88 |
var domNode = node._node, // performance cheat; getValue() is a big hit when polling |
|
|
89 |
event = options.e, |
|
|
90 |
vcData = node._data && node._data[DATA_KEY], // another perf cheat |
|
|
91 |
stopped = 0, |
|
|
92 |
facade, prevVal, newVal, nodeName, selectedOption, stopElement; |
|
|
93 |
|
|
|
94 |
if (!(domNode && vcData)) { |
|
|
95 |
Y.log('_poll: node #' + node.get('id') + ' disappeared; stopping polling and removing all notifiers.', 'warn', 'event-valuechange'); |
|
|
96 |
VC._stopPolling(node); |
|
|
97 |
return; |
|
|
98 |
} |
|
|
99 |
|
|
|
100 |
prevVal = vcData.prevVal; |
|
|
101 |
nodeName = vcData.nodeName; |
|
|
102 |
|
|
|
103 |
if (vcData.isEditable) { |
|
|
104 |
// Use innerHTML for performance |
|
|
105 |
newVal = domNode.innerHTML; |
|
|
106 |
} else if (nodeName === 'input' || nodeName === 'textarea') { |
|
|
107 |
// Use value property for performance |
|
|
108 |
newVal = domNode.value; |
|
|
109 |
} else if (nodeName === 'select') { |
|
|
110 |
// Back-compatibility with IE6 <select> element values. |
|
|
111 |
// Huge performance cheat to get past node.get('value'). |
|
|
112 |
selectedOption = domNode.options[domNode.selectedIndex]; |
|
|
113 |
newVal = selectedOption.value || selectedOption.text; |
|
|
114 |
} |
|
|
115 |
|
|
|
116 |
if (newVal !== prevVal) { |
|
|
117 |
vcData.prevVal = newVal; |
|
|
118 |
|
|
|
119 |
facade = { |
|
|
120 |
_event : event, |
|
|
121 |
currentTarget: (event && event.currentTarget) || node, |
|
|
122 |
newVal : newVal, |
|
|
123 |
prevVal : prevVal, |
|
|
124 |
target : (event && event.target) || node |
|
|
125 |
}; |
|
|
126 |
|
|
|
127 |
Y.Object.some(vcData.notifiers, function (notifier) { |
|
|
128 |
var evt = notifier.handle.evt, |
|
|
129 |
newStopped; |
|
|
130 |
|
|
|
131 |
// support e.stopPropagation() |
|
|
132 |
if (stopped !== 1) { |
|
|
133 |
notifier.fire(facade); |
|
|
134 |
} else if (evt.el === stopElement) { |
|
|
135 |
notifier.fire(facade); |
|
|
136 |
} |
|
|
137 |
|
|
|
138 |
newStopped = evt && evt._facade ? evt._facade.stopped : 0; |
|
|
139 |
|
|
|
140 |
// need to consider the condition in which there are two |
|
|
141 |
// listeners on the same element: |
|
|
142 |
// listener 1 calls e.stopPropagation() |
|
|
143 |
// listener 2 calls e.stopImmediatePropagation() |
|
|
144 |
if (newStopped > stopped) { |
|
|
145 |
stopped = newStopped; |
|
|
146 |
|
|
|
147 |
if (stopped === 1) { |
|
|
148 |
stopElement = evt.el; |
|
|
149 |
} |
|
|
150 |
} |
|
|
151 |
|
|
|
152 |
// support e.stopImmediatePropagation() |
|
|
153 |
if (stopped === 2) { |
|
|
154 |
return true; |
|
|
155 |
} |
|
|
156 |
}); |
|
|
157 |
|
|
|
158 |
VC._refreshTimeout(node); |
|
|
159 |
} |
|
|
160 |
}, |
|
|
161 |
|
|
|
162 |
/** |
|
|
163 |
Restarts the inactivity timeout for the specified node. |
|
|
164 |
|
|
|
165 |
@method _refreshTimeout |
|
|
166 |
@param {Node} node Node to refresh. |
|
|
167 |
@param {SyntheticEvent.Notifier} notifier |
|
|
168 |
@protected |
|
|
169 |
@static |
|
|
170 |
**/ |
|
|
171 |
_refreshTimeout: function (node, notifier) { |
|
|
172 |
// The node may have been destroyed, so check that it still exists |
|
|
173 |
// before trying to get its data. Otherwise an error will occur. |
|
|
174 |
if (!node._node) { |
|
|
175 |
Y.log('_stopPolling: node disappeared', 'warn', 'event-valuechange'); |
|
|
176 |
return; |
|
|
177 |
} |
|
|
178 |
|
|
|
179 |
var vcData = node.getData(DATA_KEY); |
|
|
180 |
|
|
|
181 |
VC._stopTimeout(node); // avoid dupes |
|
|
182 |
|
|
|
183 |
// If we don't see any changes within the timeout period (10 seconds by |
|
|
184 |
// default), stop polling. |
|
|
185 |
vcData.timeout = setTimeout(function () { |
|
|
186 |
Y.log('timeout: #' + node.get('id'), 'info', 'event-valuechange'); |
|
|
187 |
VC._stopPolling(node, notifier); |
|
|
188 |
}, VC.TIMEOUT); |
|
|
189 |
|
|
|
190 |
Y.log('_refreshTimeout: #' + node.get('id'), 'info', 'event-valuechange'); |
|
|
191 |
}, |
|
|
192 |
|
|
|
193 |
/** |
|
|
194 |
Begins polling for changes to the `value` property of the specified node. If |
|
|
195 |
polling is already underway for the specified node, it will not be restarted |
|
|
196 |
unless the `force` option is `true` |
|
|
197 |
|
|
|
198 |
@method _startPolling |
|
|
199 |
@param {Node} node Node to watch. |
|
|
200 |
@param {SyntheticEvent.Notifier} notifier |
|
|
201 |
|
|
|
202 |
@param {Object} options Options object. |
|
|
203 |
@param {EventFacade} [options.e] Event facade of the event that |
|
|
204 |
initiated the polling. |
|
|
205 |
@param {Boolean} [options.force=false] If `true`, polling will be |
|
|
206 |
restarted even if we're already polling this node. |
|
|
207 |
|
|
|
208 |
@protected |
|
|
209 |
@static |
|
|
210 |
**/ |
|
|
211 |
_startPolling: function (node, notifier, options) { |
|
|
212 |
var vcData, isEditable; |
|
|
213 |
|
|
|
214 |
if (!node.test('input,textarea,select') && !(isEditable = VC._isEditable(node))) { |
|
|
215 |
Y.log('_startPolling: aborting poll on #' + node.get('id') + ' -- not a detectable node', 'warn', 'event-valuechange'); |
|
|
216 |
return; |
|
|
217 |
} |
|
|
218 |
|
|
|
219 |
vcData = node.getData(DATA_KEY); |
|
|
220 |
|
|
|
221 |
if (!vcData) { |
|
|
222 |
vcData = { |
|
|
223 |
nodeName : node.get(NODE_NAME).toLowerCase(), |
|
|
224 |
isEditable : isEditable, |
|
|
225 |
prevVal : isEditable ? node.getDOMNode().innerHTML : node.get(VALUE) |
|
|
226 |
}; |
|
|
227 |
|
|
|
228 |
node.setData(DATA_KEY, vcData); |
|
|
229 |
} |
|
|
230 |
|
|
|
231 |
vcData.notifiers || (vcData.notifiers = {}); |
|
|
232 |
|
|
|
233 |
// Don't bother continuing if we're already polling this node, unless |
|
|
234 |
// `options.force` is true. |
|
|
235 |
if (vcData.interval) { |
|
|
236 |
if (options.force) { |
|
|
237 |
VC._stopPolling(node, notifier); // restart polling, but avoid dupe polls |
|
|
238 |
} else { |
|
|
239 |
vcData.notifiers[Y.stamp(notifier)] = notifier; |
|
|
240 |
return; |
|
|
241 |
} |
|
|
242 |
} |
|
|
243 |
|
|
|
244 |
// Poll for changes to the node's value. We can't rely on keyboard |
|
|
245 |
// events for this, since the value may change due to a mouse-initiated |
|
|
246 |
// paste event, an IME input event, or for some other reason that |
|
|
247 |
// doesn't trigger a key event. |
|
|
248 |
vcData.notifiers[Y.stamp(notifier)] = notifier; |
|
|
249 |
|
|
|
250 |
vcData.interval = setInterval(function () { |
|
|
251 |
VC._poll(node, options); |
|
|
252 |
}, VC.POLL_INTERVAL); |
|
|
253 |
|
|
|
254 |
Y.log('_startPolling: #' + node.get('id'), 'info', 'event-valuechange'); |
|
|
255 |
|
|
|
256 |
VC._refreshTimeout(node, notifier); |
|
|
257 |
}, |
|
|
258 |
|
|
|
259 |
/** |
|
|
260 |
Stops polling for changes to the specified node's `value` attribute. |
|
|
261 |
|
|
|
262 |
@method _stopPolling |
|
|
263 |
@param {Node} node Node to stop polling on. |
|
|
264 |
@param {SyntheticEvent.Notifier} [notifier] Notifier to remove from the |
|
|
265 |
node. If not specified, all notifiers will be removed. |
|
|
266 |
@protected |
|
|
267 |
@static |
|
|
268 |
**/ |
|
|
269 |
_stopPolling: function (node, notifier) { |
|
|
270 |
// The node may have been destroyed, so check that it still exists |
|
|
271 |
// before trying to get its data. Otherwise an error will occur. |
|
|
272 |
if (!node._node) { |
|
|
273 |
Y.log('_stopPolling: node disappeared', 'info', 'event-valuechange'); |
|
|
274 |
return; |
|
|
275 |
} |
|
|
276 |
|
|
|
277 |
var vcData = node.getData(DATA_KEY) || {}; |
|
|
278 |
|
|
|
279 |
clearInterval(vcData.interval); |
|
|
280 |
delete vcData.interval; |
|
|
281 |
|
|
|
282 |
VC._stopTimeout(node); |
|
|
283 |
|
|
|
284 |
if (notifier) { |
|
|
285 |
vcData.notifiers && delete vcData.notifiers[Y.stamp(notifier)]; |
|
|
286 |
} else { |
|
|
287 |
vcData.notifiers = {}; |
|
|
288 |
} |
|
|
289 |
|
|
|
290 |
Y.log('_stopPolling: #' + node.get('id'), 'info', 'event-valuechange'); |
|
|
291 |
}, |
|
|
292 |
|
|
|
293 |
/** |
|
|
294 |
Clears the inactivity timeout for the specified node, if any. |
|
|
295 |
|
|
|
296 |
@method _stopTimeout |
|
|
297 |
@param {Node} node |
|
|
298 |
@protected |
|
|
299 |
@static |
|
|
300 |
**/ |
|
|
301 |
_stopTimeout: function (node) { |
|
|
302 |
var vcData = node.getData(DATA_KEY) || {}; |
|
|
303 |
|
|
|
304 |
clearTimeout(vcData.timeout); |
|
|
305 |
delete vcData.timeout; |
|
|
306 |
}, |
|
|
307 |
|
|
|
308 |
/** |
|
|
309 |
Check to see if a node has editable content or not. |
|
|
310 |
|
|
|
311 |
TODO: Add additional checks to get it to work for child nodes |
|
|
312 |
that inherit "contenteditable" from parent nodes. This may be |
|
|
313 |
too computationally intensive to be placed inside of the `_poll` |
|
|
314 |
loop, however. |
|
|
315 |
|
|
|
316 |
@method _isEditable |
|
|
317 |
@param {Node} node |
|
|
318 |
@protected |
|
|
319 |
@static |
|
|
320 |
**/ |
|
|
321 |
_isEditable: function (node) { |
|
|
322 |
// Performance cheat because this is used inside `_poll` |
|
|
323 |
var domNode = node._node; |
|
|
324 |
return domNode.contentEditable === 'true' || |
|
|
325 |
domNode.contentEditable === ''; |
|
|
326 |
}, |
|
|
327 |
|
|
|
328 |
|
|
|
329 |
|
|
|
330 |
// -- Protected Static Event Handlers -------------------------------------- |
|
|
331 |
|
|
|
332 |
/** |
|
|
333 |
Stops polling when a node's blur event fires. |
|
|
334 |
|
|
|
335 |
@method _onBlur |
|
|
336 |
@param {EventFacade} e |
|
|
337 |
@param {SyntheticEvent.Notifier} notifier |
|
|
338 |
@protected |
|
|
339 |
@static |
|
|
340 |
**/ |
|
|
341 |
_onBlur: function (e, notifier) { |
|
|
342 |
VC._stopPolling(e.currentTarget, notifier); |
|
|
343 |
}, |
|
|
344 |
|
|
|
345 |
/** |
|
|
346 |
Resets a node's history and starts polling when a focus event occurs. |
|
|
347 |
|
|
|
348 |
@method _onFocus |
|
|
349 |
@param {EventFacade} e |
|
|
350 |
@param {SyntheticEvent.Notifier} notifier |
|
|
351 |
@protected |
|
|
352 |
@static |
|
|
353 |
**/ |
|
|
354 |
_onFocus: function (e, notifier) { |
|
|
355 |
var node = e.currentTarget, |
|
|
356 |
vcData = node.getData(DATA_KEY); |
|
|
357 |
|
|
|
358 |
if (!vcData) { |
|
|
359 |
vcData = { |
|
|
360 |
isEditable : VC._isEditable(node), |
|
|
361 |
nodeName : node.get(NODE_NAME).toLowerCase() |
|
|
362 |
}; |
|
|
363 |
node.setData(DATA_KEY, vcData); |
|
|
364 |
} |
|
|
365 |
|
|
|
366 |
vcData.prevVal = vcData.isEditable ? node.getDOMNode().innerHTML : node.get(VALUE); |
|
|
367 |
|
|
|
368 |
VC._startPolling(node, notifier, {e: e}); |
|
|
369 |
}, |
|
|
370 |
|
|
|
371 |
/** |
|
|
372 |
Starts polling when a node receives a keyDown event. |
|
|
373 |
|
|
|
374 |
@method _onKeyDown |
|
|
375 |
@param {EventFacade} e |
|
|
376 |
@param {SyntheticEvent.Notifier} notifier |
|
|
377 |
@protected |
|
|
378 |
@static |
|
|
379 |
**/ |
|
|
380 |
_onKeyDown: function (e, notifier) { |
|
|
381 |
VC._startPolling(e.currentTarget, notifier, {e: e}); |
|
|
382 |
}, |
|
|
383 |
|
|
|
384 |
/** |
|
|
385 |
Starts polling when an IME-related keyUp event occurs on a node. |
|
|
386 |
|
|
|
387 |
@method _onKeyUp |
|
|
388 |
@param {EventFacade} e |
|
|
389 |
@param {SyntheticEvent.Notifier} notifier |
|
|
390 |
@protected |
|
|
391 |
@static |
|
|
392 |
**/ |
|
|
393 |
_onKeyUp: function (e, notifier) { |
|
|
394 |
// These charCodes indicate that an IME has started. We'll restart |
|
|
395 |
// polling and give the IME up to 10 seconds (by default) to finish. |
|
|
396 |
if (e.charCode === 229 || e.charCode === 197) { |
|
|
397 |
VC._startPolling(e.currentTarget, notifier, { |
|
|
398 |
e : e, |
|
|
399 |
force: true |
|
|
400 |
}); |
|
|
401 |
} |
|
|
402 |
}, |
|
|
403 |
|
|
|
404 |
/** |
|
|
405 |
Starts polling when a node receives a mouseDown event. |
|
|
406 |
|
|
|
407 |
@method _onMouseDown |
|
|
408 |
@param {EventFacade} e |
|
|
409 |
@param {SyntheticEvent.Notifier} notifier |
|
|
410 |
@protected |
|
|
411 |
@static |
|
|
412 |
**/ |
|
|
413 |
_onMouseDown: function (e, notifier) { |
|
|
414 |
VC._startPolling(e.currentTarget, notifier, {e: e}); |
|
|
415 |
}, |
|
|
416 |
|
|
|
417 |
/** |
|
|
418 |
Called when the `valuechange` event receives a new subscriber. |
|
|
419 |
|
|
|
420 |
Child nodes that aren't initially available when this subscription is |
|
|
421 |
called will still fire the `valuechange` event after their data is |
|
|
422 |
collected when the delegated `focus` event is captured. This includes |
|
|
423 |
elements that haven't been inserted into the DOM yet, as well as |
|
|
424 |
elements that aren't initially `contenteditable`. |
|
|
425 |
|
|
|
426 |
@method _onSubscribe |
|
|
427 |
@param {Node} node |
|
|
428 |
@param {Subscription} sub |
|
|
429 |
@param {SyntheticEvent.Notifier} notifier |
|
|
430 |
@param {Function|String} [filter] Filter function or selector string. Only |
|
|
431 |
provided for delegate subscriptions. |
|
|
432 |
@protected |
|
|
433 |
@static |
|
|
434 |
**/ |
|
|
435 |
_onSubscribe: function (node, sub, notifier, filter) { |
|
|
436 |
var _valuechange, callbacks, isEditable, inputNodes, editableNodes; |
|
|
437 |
|
|
|
438 |
callbacks = { |
|
|
439 |
blur : VC._onBlur, |
|
|
440 |
focus : VC._onFocus, |
|
|
441 |
keydown : VC._onKeyDown, |
|
|
442 |
keyup : VC._onKeyUp, |
|
|
443 |
mousedown: VC._onMouseDown |
|
|
444 |
}; |
|
|
445 |
|
|
|
446 |
// Store a utility object on the notifier to hold stuff that needs to be |
|
|
447 |
// passed around to trigger event handlers, polling handlers, etc. |
|
|
448 |
_valuechange = notifier._valuechange = {}; |
|
|
449 |
|
|
|
450 |
if (filter) { |
|
|
451 |
// If a filter is provided, then this is a delegated subscription. |
|
|
452 |
_valuechange.delegated = true; |
|
|
453 |
|
|
|
454 |
// Add a function to the notifier that we can use to find all |
|
|
455 |
// nodes that pass the delegate filter. |
|
|
456 |
_valuechange.getNodes = function () { |
|
|
457 |
inputNodes = node.all('input,textarea,select').filter(filter); |
|
|
458 |
editableNodes = node.all('[contenteditable="true"],[contenteditable=""]').filter(filter); |
|
|
459 |
|
|
|
460 |
return inputNodes.concat(editableNodes); |
|
|
461 |
}; |
|
|
462 |
|
|
|
463 |
// Store the initial values for each descendant of the container |
|
|
464 |
// node that passes the delegate filter. |
|
|
465 |
_valuechange.getNodes().each(function (child) { |
|
|
466 |
if (!child.getData(DATA_KEY)) { |
|
|
467 |
child.setData(DATA_KEY, { |
|
|
468 |
nodeName : child.get(NODE_NAME).toLowerCase(), |
|
|
469 |
isEditable : VC._isEditable(child), |
|
|
470 |
prevVal : isEditable ? child.getDOMNode().innerHTML : child.get(VALUE) |
|
|
471 |
}); |
|
|
472 |
} |
|
|
473 |
}); |
|
|
474 |
|
|
|
475 |
notifier._handles = Y.delegate(callbacks, node, filter, null, |
|
|
476 |
notifier); |
|
|
477 |
} else { |
|
|
478 |
isEditable = VC._isEditable(node); |
|
|
479 |
// This is a normal (non-delegated) event subscription. |
|
|
480 |
if (!node.test('input,textarea,select') && !isEditable) { |
|
|
481 |
return; |
|
|
482 |
} |
|
|
483 |
|
|
|
484 |
if (!node.getData(DATA_KEY)) { |
|
|
485 |
node.setData(DATA_KEY, { |
|
|
486 |
nodeName : node.get(NODE_NAME).toLowerCase(), |
|
|
487 |
isEditable : isEditable, |
|
|
488 |
prevVal : isEditable ? node.getDOMNode().innerHTML : node.get(VALUE) |
|
|
489 |
}); |
|
|
490 |
} |
|
|
491 |
|
|
|
492 |
notifier._handles = node.on(callbacks, null, null, notifier); |
|
|
493 |
} |
|
|
494 |
}, |
|
|
495 |
|
|
|
496 |
/** |
|
|
497 |
Called when the `valuechange` event loses a subscriber. |
|
|
498 |
|
|
|
499 |
@method _onUnsubscribe |
|
|
500 |
@param {Node} node |
|
|
501 |
@param {Subscription} subscription |
|
|
502 |
@param {SyntheticEvent.Notifier} notifier |
|
|
503 |
@protected |
|
|
504 |
@static |
|
|
505 |
**/ |
|
|
506 |
_onUnsubscribe: function (node, subscription, notifier) { |
|
|
507 |
var _valuechange = notifier._valuechange; |
|
|
508 |
|
|
|
509 |
notifier._handles && notifier._handles.detach(); |
|
|
510 |
|
|
|
511 |
if (_valuechange.delegated) { |
|
|
512 |
_valuechange.getNodes().each(function (child) { |
|
|
513 |
VC._stopPolling(child, notifier); |
|
|
514 |
}); |
|
|
515 |
} else { |
|
|
516 |
VC._stopPolling(node, notifier); |
|
|
517 |
} |
|
|
518 |
} |
|
|
519 |
}; |
|
|
520 |
|
|
|
521 |
/** |
|
|
522 |
Synthetic event that fires when the `value` property of an `<input>`, |
|
|
523 |
`<textarea>`, `<select>`, or `[contenteditable="true"]` node changes as a |
|
|
524 |
result of a user-initiated keystroke, mouse operation, or input method |
|
|
525 |
editor (IME) input event. |
|
|
526 |
|
|
|
527 |
Unlike the `onchange` event, this event fires when the value actually changes |
|
|
528 |
and not when the element loses focus. This event also reports IME and |
|
|
529 |
multi-stroke input more reliably than `oninput` or the various key events across |
|
|
530 |
browsers. |
|
|
531 |
|
|
|
532 |
For performance reasons, only focused nodes are monitored for changes, so |
|
|
533 |
programmatic value changes on nodes that don't have focus won't be detected. |
|
|
534 |
|
|
|
535 |
@example |
|
|
536 |
|
|
|
537 |
YUI().use('event-valuechange', function (Y) { |
|
|
538 |
Y.one('#my-input').on('valuechange', function (e) { |
|
|
539 |
Y.log('previous value: ' + e.prevVal); |
|
|
540 |
Y.log('new value: ' + e.newVal); |
|
|
541 |
}); |
|
|
542 |
}); |
|
|
543 |
|
|
|
544 |
@event valuechange |
|
|
545 |
@param {String} prevVal Previous value prior to the latest change. |
|
|
546 |
@param {String} newVal New value after the latest change. |
|
|
547 |
@for YUI |
|
|
548 |
**/ |
|
|
549 |
|
|
|
550 |
config = { |
|
|
551 |
detach: VC._onUnsubscribe, |
|
|
552 |
on : VC._onSubscribe, |
|
|
553 |
|
|
|
554 |
delegate : VC._onSubscribe, |
|
|
555 |
detachDelegate: VC._onUnsubscribe, |
|
|
556 |
|
|
|
557 |
publishConfig: { |
|
|
558 |
emitFacade: true |
|
|
559 |
} |
|
|
560 |
}; |
|
|
561 |
|
|
|
562 |
Y.Event.define('valuechange', config); |
|
|
563 |
Y.Event.define('valueChange', config); // deprecated, but supported for backcompat |
|
|
564 |
|
|
|
565 |
Y.ValueChange = VC; |
|
|
566 |
|
|
|
567 |
|
|
|
568 |
}, '@VERSION@', {"requires": ["event-focus", "event-synthetic"]}); |