Skip to content

Commit 15238d2

Browse files
ChristophWieskecrisbeto
authored andcommitted
fix(material/input): Number input not changing on wheel interaction (#29449)
In blink and webkit browsers the default behavior of increasing or decreasing a focused number input on wheel events is broken until a wheel event listener is explicitly added. Fixes #29074 (cherry picked from commit 10da6c6)
1 parent cb3433a commit 15238d2

File tree

5 files changed

+58
-3
lines changed

5 files changed

+58
-3
lines changed

src/dev-app/input/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ng_module(
2121
"//src/material/input",
2222
"//src/material/tabs",
2323
"//src/material/toolbar",
24+
"//src/material/tooltip",
2425
"@npm//@angular/forms",
2526
],
2627
)

src/dev-app/input/input-demo.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@
5858
</mat-card-content>
5959
</mat-card>
6060

61+
<mat-card class="demo-card demo-number-input-tooltip">
62+
<mat-toolbar color="primary">Number Input + Tooltip</mat-toolbar>
63+
<mat-card-content>
64+
<form>
65+
<mat-form-field>
66+
<mat-label>Pump Flow</mat-label>
67+
<input matNativeControl value="10" type="number"><!-- -->
68+
<span matSuffix matTooltip="Milliliter per Minute">ml/min</span>
69+
</mat-form-field>
70+
</form>
71+
</mat-card-content>
72+
</mat-card>
73+
6174
<mat-card class="demo-card demo-basic">
6275
<mat-toolbar color="primary">Error messages</mat-toolbar>
6376
<mat-card-content>

src/dev-app/input/input-demo.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {MatCheckboxModule} from '@angular/material/checkbox';
2828
import {MatIconModule} from '@angular/material/icon';
2929
import {MatTabsModule} from '@angular/material/tabs';
3030
import {MatToolbarModule} from '@angular/material/toolbar';
31+
import {MatTooltipModule} from '@angular/material/tooltip';
3132

3233
let max = 5;
3334

@@ -55,6 +56,7 @@ const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA
5556
FormFieldCustomControlExample,
5657
MyTelInput,
5758
ReactiveFormsModule,
59+
MatTooltipModule,
5860
],
5961
})
6062
export class InputDemo {

src/material/input/input.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export class MatInput
8787
private _inputValueAccessor: {value: any};
8888
private _previousPlaceholder: string | null;
8989
private _errorStateTracker: _ErrorStateTracker;
90+
private _webkitBlinkWheelListenerAttached = false;
9091

9192
/** Whether the component is being rendered on the server. */
9293
readonly _isServer: boolean;
@@ -197,6 +198,8 @@ export class MatInput
197198
if (!this._isTextarea && getSupportedInputTypes().has(this._type)) {
198199
(this._elementRef.nativeElement as HTMLInputElement).type = this._type;
199200
}
201+
202+
this._ensureWheelDefaultBehavior();
200203
}
201204
protected _type = 'text';
202205

@@ -266,7 +269,7 @@ export class MatInput
266269
defaultErrorStateMatcher: ErrorStateMatcher,
267270
@Optional() @Self() @Inject(MAT_INPUT_VALUE_ACCESSOR) inputValueAccessor: any,
268271
private _autofillMonitor: AutofillMonitor,
269-
ngZone: NgZone,
272+
private _ngZone: NgZone,
270273
// TODO: Remove this once the legacy appearance has been removed. We only need
271274
// to inject the form field for determining whether the placeholder has been promoted.
272275
@Optional() @Inject(MAT_FORM_FIELD) protected _formField?: MatFormField,
@@ -287,7 +290,7 @@ export class MatInput
287290
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
288291
// exists on iOS, we only bother to install the listener on iOS.
289292
if (_platform.IOS) {
290-
ngZone.runOutsideAngular(() => {
293+
_ngZone.runOutsideAngular(() => {
291294
_elementRef.nativeElement.addEventListener('keyup', this._iOSKeyupListener);
292295
});
293296
}
@@ -334,6 +337,10 @@ export class MatInput
334337
if (this._platform.IOS) {
335338
this._elementRef.nativeElement.removeEventListener('keyup', this._iOSKeyupListener);
336339
}
340+
341+
if (this._webkitBlinkWheelListenerAttached) {
342+
this._elementRef.nativeElement.removeEventListener('wheel', this._webkitBlinkWheelListener);
343+
}
337344
}
338345

339346
ngDoCheck() {
@@ -527,4 +534,36 @@ export class MatInput
527534
el.setSelectionRange(0, 0);
528535
}
529536
};
537+
538+
private _webkitBlinkWheelListener = (): void => {
539+
// This is a noop function and is used to enable mouse wheel input
540+
// on number inputs
541+
// on blink and webkit browsers.
542+
};
543+
544+
/**
545+
* In blink and webkit browsers a focused number input does not increment or decrement its value
546+
* on mouse wheel interaction unless a wheel event listener is attached to it or one of its ancestors or a passive wheel listener is attached somewhere in the DOM.
547+
* For example: Hitting a tooltip once enables the mouse wheel input for all number inputs as long as it exists.
548+
* In order to get reliable and intuitive behavior we apply a wheel event on our own
549+
* thus making sure increment and decrement by mouse wheel works every time.
550+
* @docs-private
551+
*/
552+
private _ensureWheelDefaultBehavior(): void {
553+
if (
554+
!this._webkitBlinkWheelListenerAttached &&
555+
this._type === 'number' &&
556+
(this._platform.BLINK || this._platform.WEBKIT)
557+
) {
558+
this._ngZone.runOutsideAngular(() => {
559+
this._elementRef.nativeElement.addEventListener('wheel', this._webkitBlinkWheelListener);
560+
});
561+
this._webkitBlinkWheelListenerAttached = true;
562+
}
563+
564+
if (this._webkitBlinkWheelListenerAttached && this._type !== 'number') {
565+
this._elementRef.nativeElement.removeEventListener('wheel', this._webkitBlinkWheelListener);
566+
this._webkitBlinkWheelListenerAttached = true;
567+
}
568+
}
530569
}

tools/public_api_guard/material/input.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export { MatHint }
4747

4848
// @public (undocumented)
4949
export class MatInput implements MatFormFieldControl<any>, OnChanges, OnDestroy, AfterViewInit, DoCheck {
50-
constructor(_elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>, _platform: Platform, ngControl: NgControl, parentForm: NgForm, parentFormGroup: FormGroupDirective, defaultErrorStateMatcher: ErrorStateMatcher, inputValueAccessor: any, _autofillMonitor: AutofillMonitor, ngZone: NgZone, _formField?: MatFormField | undefined);
50+
constructor(_elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>, _platform: Platform, ngControl: NgControl, parentForm: NgForm, parentFormGroup: FormGroupDirective, defaultErrorStateMatcher: ErrorStateMatcher, inputValueAccessor: any, _autofillMonitor: AutofillMonitor, _ngZone: NgZone, _formField?: MatFormField | undefined);
5151
autofilled: boolean;
5252
controlType: string;
5353
protected _dirtyCheckNativeValue(): void;

0 commit comments

Comments
 (0)