Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 55d9db5

Browse files
jbedardcaitp
authored andcommitted
fix(inputs): ignoring input events in IE caused by placeholder changes or focus/blur on inputs with placeholders
Closes #9265
1 parent 579aa59 commit 55d9db5

File tree

5 files changed

+164
-23
lines changed

5 files changed

+164
-23
lines changed

Diff for: src/ng/directive/input.js

-10
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,6 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
954954
}
955955

956956
function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
957-
var placeholder = element[0].placeholder, noevent = {};
958957
var type = lowercase(element[0].type);
959958

960959
// In composition mode, users are still inputing intermediate text buffer,
@@ -978,15 +977,6 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
978977
var value = element.val(),
979978
event = ev && ev.type;
980979

981-
// IE (11 and under) seem to emit an 'input' event if the placeholder value changes.
982-
// We don't want to dirty the value when this happens, so we abort here. Unfortunately,
983-
// IE also sends input events for other non-input-related things, (such as focusing on a
984-
// form control), so this change is not entirely enough to solve this.
985-
if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) {
986-
placeholder = element[0].placeholder;
987-
return;
988-
}
989-
990980
// By default we will trim the value
991981
// If the attribute ng-trim exists we will avoid trimming
992982
// If input type is 'password', the value is never trimmed

Diff for: src/ng/sniffer.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ function $SnifferProvider() {
6767
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
6868
// it. In particular the event is not fired when backspace or delete key are pressed or
6969
// when cut operation is performed.
70-
if (event == 'input' && msie == 9) return false;
70+
// IE10+ implements 'input' event but it erroneously fires under various situations,
71+
// e.g. when placeholder changes, or a form is focused.
72+
if (event === 'input' && msie <= 11) return false;
7173

7274
if (isUndefined(eventSupport[event])) {
7375
var divElm = document.createElement('div');

Diff for: src/ngScenario/dsl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ angular.scenario.dsl('binding', function() {
199199
*/
200200
angular.scenario.dsl('input', function() {
201201
var chain = {};
202-
var supportInputEvent = 'oninput' in document.createElement('div') && msie != 9;
202+
var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
203203

204204
chain.enter = function(value, event) {
205205
return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",

Diff for: test/ng/directive/inputSpec.js

+158-10
Original file line numberDiff line numberDiff line change
@@ -1549,22 +1549,170 @@ describe('input', function() {
15491549
expect(scope.name).toEqual('caitp');
15501550
});
15511551

1552-
it('should not dirty the model on an input event in response to a placeholder change', inject(function($sniffer) {
1553-
if (msie && $sniffer.hasEvent('input')) {
1554-
compileInput('<input type="text" ng-model="name" name="name" />');
1555-
inputElm.attr('placeholder', 'Test');
1556-
browserTrigger(inputElm, 'input');
1557-
1552+
describe("IE placeholder input events", function() {
1553+
//IE fires an input event whenever a placeholder visually changes, essentially treating it as a value
1554+
//Events:
1555+
// placeholder attribute change: *input*
1556+
// focus (which visually removes the placeholder value): focusin focus *input*
1557+
// blur (which visually creates the placeholder value): focusout *input* blur
1558+
//However none of these occur if the placeholder is not visible at the time of the event.
1559+
//These tests try simulate various scenerios which do/do-not fire the extra input event
1560+
1561+
it('should not dirty the model on an input event in response to a placeholder change', function() {
1562+
compileInput('<input type="text" placeholder="Test" attr-capture ng-model="unsetValue" name="name" />');
1563+
msie && browserTrigger(inputElm, 'input');
15581564
expect(inputElm.attr('placeholder')).toBe('Test');
15591565
expect(inputElm).toBePristine();
15601566

1561-
inputElm.attr('placeholder', 'Test Again');
1562-
browserTrigger(inputElm, 'input');
1567+
attrs.$set('placeholder', '');
1568+
msie && browserTrigger(inputElm, 'input');
1569+
expect(inputElm.attr('placeholder')).toBe('');
1570+
expect(inputElm).toBePristine();
15631571

1572+
attrs.$set('placeholder', 'Test Again');
1573+
msie && browserTrigger(inputElm, 'input');
15641574
expect(inputElm.attr('placeholder')).toBe('Test Again');
15651575
expect(inputElm).toBePristine();
1566-
}
1567-
}));
1576+
1577+
attrs.$set('placeholder', undefined);
1578+
msie && browserTrigger(inputElm, 'input');
1579+
expect(inputElm.attr('placeholder')).toBe(undefined);
1580+
expect(inputElm).toBePristine();
1581+
1582+
changeInputValueTo('foo');
1583+
expect(inputElm).toBeDirty();
1584+
});
1585+
1586+
it('should not dirty the model on an input event in response to a interpolated placeholder change', inject(function($rootScope) {
1587+
compileInput('<input type="text" placeholder="{{ph}}" ng-model="unsetValue" name="name" />');
1588+
msie && browserTrigger(inputElm, 'input');
1589+
expect(inputElm).toBePristine();
1590+
1591+
$rootScope.ph = 1;
1592+
$rootScope.$digest();
1593+
msie && browserTrigger(inputElm, 'input');
1594+
expect(inputElm).toBePristine();
1595+
1596+
$rootScope.ph = "";
1597+
$rootScope.$digest();
1598+
msie && browserTrigger(inputElm, 'input');
1599+
expect(inputElm).toBePristine();
1600+
1601+
changeInputValueTo('foo');
1602+
expect(inputElm).toBeDirty();
1603+
}));
1604+
1605+
it('should dirty the model on an input event while in focus even if the placeholder changes', inject(function($rootScope) {
1606+
$rootScope.ph = 'Test';
1607+
compileInput('<input type="text" ng-attr-placeholder="{{ph}}" ng-model="unsetValue" name="name" />');
1608+
expect(inputElm).toBePristine();
1609+
1610+
browserTrigger(inputElm, 'focusin');
1611+
browserTrigger(inputElm, 'focus');
1612+
msie && browserTrigger(inputElm, 'input');
1613+
expect(inputElm.attr('placeholder')).toBe('Test');
1614+
expect(inputElm).toBePristine();
1615+
1616+
$rootScope.ph = 'Test Again';
1617+
$rootScope.$digest();
1618+
expect(inputElm).toBePristine();
1619+
1620+
changeInputValueTo('foo');
1621+
expect(inputElm).toBeDirty();
1622+
}));
1623+
1624+
it('should not dirty the model on an input event in response to a ng-attr-placeholder change', inject(function($rootScope) {
1625+
compileInput('<input type="text" ng-attr-placeholder="{{ph}}" ng-model="unsetValue" name="name" />');
1626+
expect(inputElm).toBePristine();
1627+
1628+
$rootScope.ph = 1;
1629+
$rootScope.$digest();
1630+
msie && browserTrigger(inputElm, 'input');
1631+
expect(inputElm).toBePristine();
1632+
1633+
$rootScope.ph = "";
1634+
$rootScope.$digest();
1635+
msie && browserTrigger(inputElm, 'input');
1636+
expect(inputElm).toBePristine();
1637+
1638+
changeInputValueTo('foo');
1639+
expect(inputElm).toBeDirty();
1640+
}));
1641+
1642+
it('should not dirty the model on an input event in response to a focus', inject(function($sniffer) {
1643+
compileInput('<input type="text" placeholder="Test" ng-model="unsetValue" name="name" />');
1644+
msie && browserTrigger(inputElm, 'input');
1645+
expect(inputElm.attr('placeholder')).toBe('Test');
1646+
expect(inputElm).toBePristine();
1647+
1648+
browserTrigger(inputElm, 'focusin');
1649+
browserTrigger(inputElm, 'focus');
1650+
msie && browserTrigger(inputElm, 'input');
1651+
expect(inputElm.attr('placeholder')).toBe('Test');
1652+
expect(inputElm).toBePristine();
1653+
1654+
changeInputValueTo('foo');
1655+
expect(inputElm).toBeDirty();
1656+
}));
1657+
1658+
it('should not dirty the model on an input event in response to a blur', inject(function($sniffer) {
1659+
compileInput('<input type="text" placeholder="Test" ng-model="unsetValue" name="name" />');
1660+
msie && browserTrigger(inputElm, 'input');
1661+
expect(inputElm.attr('placeholder')).toBe('Test');
1662+
expect(inputElm).toBePristine();
1663+
1664+
browserTrigger(inputElm, 'focusin');
1665+
browserTrigger(inputElm, 'focus');
1666+
msie && browserTrigger(inputElm, 'input');
1667+
expect(inputElm).toBePristine();
1668+
1669+
browserTrigger(inputElm, 'focusout');
1670+
msie && browserTrigger(inputElm, 'input');
1671+
browserTrigger(inputElm, 'blur');
1672+
expect(inputElm).toBePristine();
1673+
1674+
changeInputValueTo('foo');
1675+
expect(inputElm).toBeDirty();
1676+
}));
1677+
1678+
it('should dirty the model on an input event if there is a placeholder and value', inject(function($rootScope) {
1679+
$rootScope.name = 'foo';
1680+
compileInput('<input type="text" placeholder="Test" ng-model="name" value="init" name="name" />');
1681+
expect(inputElm.val()).toBe($rootScope.name);
1682+
expect(inputElm).toBePristine();
1683+
1684+
changeInputValueTo('bar');
1685+
expect(inputElm).toBeDirty();
1686+
}));
1687+
1688+
it('should dirty the model on an input event if there is a placeholder and value after focusing', inject(function($rootScope) {
1689+
$rootScope.name = 'foo';
1690+
compileInput('<input type="text" placeholder="Test" ng-model="name" value="init" name="name" />');
1691+
expect(inputElm.val()).toBe($rootScope.name);
1692+
expect(inputElm).toBePristine();
1693+
1694+
browserTrigger(inputElm, 'focusin');
1695+
browserTrigger(inputElm, 'focus');
1696+
changeInputValueTo('bar');
1697+
expect(inputElm).toBeDirty();
1698+
}));
1699+
1700+
it('should dirty the model on an input event if there is a placeholder and value after bluring', inject(function($rootScope) {
1701+
$rootScope.name = 'foo';
1702+
compileInput('<input type="text" placeholder="Test" ng-model="name" value="init" name="name" />');
1703+
expect(inputElm.val()).toBe($rootScope.name);
1704+
expect(inputElm).toBePristine();
1705+
1706+
browserTrigger(inputElm, 'focusin');
1707+
browserTrigger(inputElm, 'focus');
1708+
expect(inputElm).toBePristine();
1709+
1710+
browserTrigger(inputElm, 'focusout');
1711+
browserTrigger(inputElm, 'blur');
1712+
changeInputValueTo('bar');
1713+
expect(inputElm).toBeDirty();
1714+
}));
1715+
});
15681716

15691717

15701718
it('should interpolate input names', function() {

Diff for: test/ng/snifferSpec.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ describe('$sniffer', function() {
6464

6565
it('should claim that IE9 doesn\'t have support for "oninput"', function() {
6666
// IE9 implementation is fubared, so it's better to pretend that it doesn't have the support
67+
// IE10+ implementation is fubared when mixed with placeholders
6768
mockDivElement = {oninput: noop};
6869

69-
expect($sniffer.hasEvent('input')).toBe((msie == 9) ? false : true);
70+
expect($sniffer.hasEvent('input')).toBe(!(msie && msie <= 11));
7071
});
7172
});
7273

0 commit comments

Comments
 (0)