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