Skip to content

Commit d393c8b

Browse files
committed
Spinner: Handle async focus events in IE. Fixes incorrect detection of changes.
1 parent ab4d8b7 commit d393c8b

File tree

2 files changed

+90
-48
lines changed

2 files changed

+90
-48
lines changed

tests/unit/spinner/spinner_events.js

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ test( "stop", function() {
121121
element.spinner( "value", 999 );
122122
});
123123

124-
test( "change", function() {
124+
asyncTest( "change", function() {
125125
expect( 14 );
126126
var element = $( "#spin" ).spinner();
127127

@@ -174,50 +174,56 @@ test( "change", function() {
174174
shouldChange( false, "button up, before blur" );
175175
element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup();
176176
shouldChange( true, "blur after button up" );
177-
element.blur();
178-
179-
shouldChange( false, "button down, before blur" );
180-
element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup();
181-
shouldChange( true, "blur after button down" );
182-
element.blur();
183-
184-
shouldChange( false, "many buttons, same final value, before blur" );
185-
element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup();
186-
element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup();
187-
element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup();
188-
element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup();
189-
shouldChange( false, "blur after many buttons, same final value" );
190-
element.blur();
191-
192-
shouldChange( true, "stepUp" );
193-
element.spinner( "stepUp" );
194-
195-
shouldChange( true, "stepDown" );
196-
element.spinner( "stepDown" );
197-
198-
shouldChange( true, "pageUp" );
199-
element.spinner( "pageUp" );
200-
201-
shouldChange( true, "pageDown" );
202-
element.spinner( "pageDown" );
203-
204-
shouldChange( true, "value" );
205-
element.spinner( "value", 999 );
206-
207-
shouldChange( false, "value, same value" );
208-
element.spinner( "value", 999 );
209-
210-
shouldChange( true, "max, value changed" );
211-
element.spinner( "option", "max", 900 );
212-
213-
shouldChange( false, "max, value not changed" );
214-
element.spinner( "option", "max", 1000 );
215-
216-
shouldChange( true, "min, value changed" );
217-
element.spinner( "option", "min", 950 );
218-
219-
shouldChange( false, "min, value not changed" );
220-
element.spinner( "option", "min", 200 );
177+
setTimeout(function() {
178+
element.blur();
179+
180+
shouldChange( false, "button down, before blur" );
181+
element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup();
182+
shouldChange( true, "blur after button down" );
183+
setTimeout(function() {
184+
element.blur();
185+
186+
shouldChange( false, "many buttons, same final value, before blur" );
187+
element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup();
188+
element.spinner( "widget" ).find( ".ui-spinner-up" ).mousedown().mouseup();
189+
element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup();
190+
element.spinner( "widget" ).find( ".ui-spinner-down" ).mousedown().mouseup();
191+
shouldChange( false, "blur after many buttons, same final value" );
192+
element.blur();
193+
setTimeout(function() {
194+
shouldChange( true, "stepUp" );
195+
element.spinner( "stepUp" );
196+
197+
shouldChange( true, "stepDown" );
198+
element.spinner( "stepDown" );
199+
200+
shouldChange( true, "pageUp" );
201+
element.spinner( "pageUp" );
202+
203+
shouldChange( true, "pageDown" );
204+
element.spinner( "pageDown" );
205+
206+
shouldChange( true, "value" );
207+
element.spinner( "value", 999 );
208+
209+
shouldChange( false, "value, same value" );
210+
element.spinner( "value", 999 );
211+
212+
shouldChange( true, "max, value changed" );
213+
element.spinner( "option", "max", 900 );
214+
215+
shouldChange( false, "max, value not changed" );
216+
element.spinner( "option", "max", 1000 );
217+
218+
shouldChange( true, "min, value changed" );
219+
element.spinner( "option", "min", 950 );
220+
221+
shouldChange( false, "min, value not changed" );
222+
element.spinner( "option", "min", 200 );
223+
start();
224+
});
225+
});
226+
});
221227
});
222228

223229
})( jQuery );

ui/jquery.ui.spinner.js

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ $.widget( "ui.spinner", {
9393
this.previous = this.element.val();
9494
},
9595
blur: function( event ) {
96+
if ( this.cancelBlur ) {
97+
delete this.cancelBlur;
98+
return;
99+
}
100+
96101
this._refresh();
97102
this.uiSpinner.removeClass( "ui-state-active" );
98103
if ( this.previous !== this.element.val() ) {
@@ -117,11 +122,42 @@ $.widget( "ui.spinner", {
117122
event.preventDefault();
118123
},
119124
"mousedown .ui-spinner-button": function( event ) {
125+
var previous;
126+
127+
// We never want the buttons to have focus; whenever the user is
128+
// interacting with the spinner, the focus should be on the input.
129+
// If the input is focused then this.previous is properly set from
130+
// when the input first received focus. If the input is not focused
131+
// then we need to set this.previous based on the value before spinning.
132+
previous = this.element[0] === this.document[0].activeElement ?
133+
this.previous : this.element.val();
134+
function checkFocus() {
135+
var isActive = this.element[0] === this.document[0].activeElement;
136+
if ( !isActive ) {
137+
this.element.focus();
138+
this.previous = previous;
139+
// support: IE
140+
// IE sets focus asynchronously, so we need to check if focus
141+
// moved off of the input because the user clicked on the button.
142+
this._delay(function() {
143+
this.previous = previous;
144+
});
145+
}
146+
}
147+
120148
// ensure focus is on (or stays on) the text field
121149
event.preventDefault();
122-
if ( this.document[0].activeElement !== this.element[ 0 ] ) {
123-
this.element.focus();
124-
}
150+
checkFocus.call( this );
151+
152+
// support: IE
153+
// IE doesn't prevent moving focus even with event.preventDefault()
154+
// so we set a flag to know when we should ignore the blur event
155+
// and check (again) if focus moved off of the input.
156+
this.cancelBlur = true;
157+
this._delay(function() {
158+
delete this.cancelBlur;
159+
checkFocus.call( this );
160+
});
125161

126162
if ( this._start( event ) === false ) {
127163
return;

0 commit comments

Comments
 (0)