Skip to content

Commit

Permalink
fix(slider): stop dragging if page loses focus (#17849)
Browse files Browse the repository at this point in the history
If the page loses focus (e.g. from an `alert` or the window being minimized) while the user is dragging a slider, it'll be stuck as dragging until the user comes back and clicks somewhere which might look weird and change the value. These changes stop the dragging sequence once the page is blurred.
  • Loading branch information
crisbeto authored and jelbourn committed Dec 3, 2019
1 parent 2e6045c commit 3b3c2ca
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/material/slider/slider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,27 @@ describe('MatSlider', () => {
expect(sliderNativeElement.classList).not.toContain('mat-slider-sliding');
});

it('should stop dragging if the page loses focus', () => {
const classlist = sliderNativeElement.classList;

expect(classlist).not.toContain('mat-slider-sliding');

dispatchSlideStartEvent(sliderNativeElement, 0);
fixture.detectChanges();

expect(classlist).toContain('mat-slider-sliding');

dispatchSlideEvent(sliderNativeElement, 0.34);
fixture.detectChanges();

expect(classlist).toContain('mat-slider-sliding');

dispatchFakeEvent(window, 'blur');
fixture.detectChanges();

expect(classlist).not.toContain('mat-slider-sliding');
});

it('should reset active state upon blur', () => {
sliderInstance._isActive = true;

Expand Down
23 changes: 22 additions & 1 deletion src/material/slider/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ export class MatSlider extends _MatSliderMixinBase
return (this._dir && this._dir.value == 'rtl') ? 'rtl' : 'ltr';
}

/** Keeps track of the last pointer event that was captured by the slider. */
private _lastPointerEvent: MouseEvent | TouchEvent | null;

constructor(elementRef: ElementRef,
private _focusMonitor: FocusMonitor,
private _changeDetectorRef: ChangeDetectorRef,
Expand Down Expand Up @@ -513,6 +516,7 @@ export class MatSlider extends _MatSliderMixinBase
const element = this._elementRef.nativeElement;
element.removeEventListener('mousedown', this._pointerDown, activeEventOptions);
element.removeEventListener('touchstart', this._pointerDown, activeEventOptions);
this._lastPointerEvent = null;
this._removeGlobalEvents();
this._focusMonitor.stopMonitoring(this._elementRef);
this._dirChangeSubscription.unsubscribe();
Expand Down Expand Up @@ -611,6 +615,7 @@ export class MatSlider extends _MatSliderMixinBase
const oldValue = this.value;
const pointerPosition = getPointerPositionOnPage(event);
this._isSliding = true;
this._lastPointerEvent = event;
event.preventDefault();
this._focusHostElement();
this._onMouseenter(); // Simulate mouseenter in case this is a mobile device.
Expand All @@ -637,6 +642,7 @@ export class MatSlider extends _MatSliderMixinBase
// Prevent the slide from selecting anything else.
event.preventDefault();
const oldValue = this.value;
this._lastPointerEvent = event;
this._updateValueFromPosition(getPointerPositionOnPage(event));

// Native range elements always emit `input` events when the value changed while sliding.
Expand All @@ -654,7 +660,7 @@ export class MatSlider extends _MatSliderMixinBase

event.preventDefault();
this._removeGlobalEvents();
this._valueOnSlideStart = this._pointerPositionOnStart = null;
this._valueOnSlideStart = this._pointerPositionOnStart = this._lastPointerEvent = null;
this._isSliding = false;

if (this._valueOnSlideStart != this.value && !this.disabled &&
Expand All @@ -665,6 +671,15 @@ export class MatSlider extends _MatSliderMixinBase
}
}

/** Called when the window has lost focus. */
private _windowBlur = () => {
// If the window is blurred while dragging we need to stop dragging because the
// browser won't dispatch the `mouseup` and `touchend` events anymore.
if (this._lastPointerEvent) {
this._pointerUp(this._lastPointerEvent);
}
}

/**
* Binds our global move and end events. They're bound at the document level and only while
* dragging so that the user doesn't have to keep their pointer exactly over the slider
Expand All @@ -683,6 +698,9 @@ export class MatSlider extends _MatSliderMixinBase
body.addEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
}
if (typeof window !== 'undefined' && window) {
window.addEventListener('blur', this._windowBlur);
}
}

/** Removes any global event listeners that we may have added. */
Expand All @@ -695,6 +713,9 @@ export class MatSlider extends _MatSliderMixinBase
body.removeEventListener('touchend', this._pointerUp, activeEventOptions);
body.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
if (typeof window !== 'undefined' && window) {
window.removeEventListener('blur', this._windowBlur);
}
}

/** Increments the slider by the given number of steps (negative number decrements). */
Expand Down

0 comments on commit 3b3c2ca

Please sign in to comment.