在 Firefox 中,Angular 模型在焦点离开下拉列表之前不会更新。标签时如何正确设置焦点?
In Firefox, Angular model doesn't update until focus leaves dropdown. How to properly set focus when tabbing?
我有一个 select
元素。选定的值确定文本 input
框是否随后显示(使用 ng-show
)。出于可访问性目的,我希望用户能够通过输入无缝切换;一旦他们离开 select
,我希望新输入框成为焦点。
<div ng-controller="MyCtrl">
<label>Start here, then tab through the inputs:</label>
<div>
<input type="text" id="firstInput" placeholder="(my first input)" />
</div>
<label>Do you need another input box before moving on?</label>
<div>
<select ng-model="needAnotherInput">
<option value="false">No, I do not</option>
<option value="true">Yes, I do</option>
</select>
</div>
<div>
<input ng-show="needAnotherInput" type="text" />
</div>
<label>Here's the next thing to fill out:</label>
<div>
<input type="text" />
</div>
</div>
JSFiddle: http://jsfiddle.net/127k2urr/9/
您会在 Chrome 和 IE 中看到,这工作正常;如果您选择下拉菜单并用箭头浏览选项(不要打开下拉菜单),模型会随着每个新选择而更新。但是在 Firefox 中,模型不会更新,直到您离开下拉列表,所以新的输入框出现得太晚了;焦点转到下一个。
根据 this discussion on the AngularJS GitHub, this might be related to when the browser fires the onchange event, and Firefox may be doing it properly according to spec. Regardless, I want to deal with these cases gracefully and in a generalizable way. Any solution also must account for SHIFT+TAB, which takes you backwards in the DOM through the focusable elements. In another Whosebug thread,有人给出了一个指令示例,该指令将在元素出现时立即将焦点设置到该元素。我会尝试在有时可见的下一个输入中添加类似的内容并报告回来。
我知道这个解决方案是 jQuery 解决方案,但它代表了一种您可以在 Angular.js 中实现的技术。
解决方法是在 UI 更新时用 "busy popup" 拦截焦点,然后将焦点移动到 next/previous 字段。
一个示例实现是这个页面:http://dylanb.github.io/a11yvalid/jqvalid9.html
如果您输入的出生日期小于 18 岁,它将模拟服务器调用(超时),然后使用监护人信息的新字段更新表单。
代码在此存储库中 https://github.com/dylanb/a11yvalid
可在此处找到错误实施的示例:http://dylanb.github.io/a11yvalid/jqvalid8.html
[太平洋标准时间下午 1 点:编辑为在*任何* keydown 事件上触发 change(),而不仅仅是 UP 或 DOWN,因为用户还可以通过按字符键来浏览选项,例如字母。]
[太平洋标准时间下午 2 点:请注意,这使用 JQuery 来触发处理程序。如果你有Angular with JQLite,你可以使用element.triggerHandler('change')
.]
我经历了几次 'focus me when I show up (if these conditions are met) (after this amount of time)' 的迭代,但所有这些迭代都在一种或另一种浏览器中提供了糟糕的屏幕阅读器体验。
所以我最终采用了显而易见的方法:我将一个 keydown 处理程序附加到 select
,当按下任何键时它会触发 change() 事件。
.directive("updateOnKeydown", ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
// updates model when a key is pressed; now, when a keyboard user
// tabs out of the dropdown after using the arrows/letter keys to make
// a selection, the DOM is up-to-date and focus will be set to the
// correct element
element.bind('keydown', function (evt) {
evt = (!evt) ? window.event : evt;
var keyCode = evt.keyCode || evt.charCode;
if (keyCode) { // do we want to exclude anything?
if (!scope.$$phase) {
$timeout(function () {
$(element).change();
});
}
else {
console.log('in the middle of something!');
}
}
});
}
}
}])
和...
<select ng-model="needAnotherInput" update-on-keydown>
它在我需要的任何地方都运行良好,但如果您愿意,建议进行一些调整。 (我应该从 keydown 处理程序返回一些东西吗?如果它处于摘要循环的中间,我应该再试一次吗?)
我有一个 select
元素。选定的值确定文本 input
框是否随后显示(使用 ng-show
)。出于可访问性目的,我希望用户能够通过输入无缝切换;一旦他们离开 select
,我希望新输入框成为焦点。
<div ng-controller="MyCtrl">
<label>Start here, then tab through the inputs:</label>
<div>
<input type="text" id="firstInput" placeholder="(my first input)" />
</div>
<label>Do you need another input box before moving on?</label>
<div>
<select ng-model="needAnotherInput">
<option value="false">No, I do not</option>
<option value="true">Yes, I do</option>
</select>
</div>
<div>
<input ng-show="needAnotherInput" type="text" />
</div>
<label>Here's the next thing to fill out:</label>
<div>
<input type="text" />
</div>
</div>
JSFiddle: http://jsfiddle.net/127k2urr/9/
您会在 Chrome 和 IE 中看到,这工作正常;如果您选择下拉菜单并用箭头浏览选项(不要打开下拉菜单),模型会随着每个新选择而更新。但是在 Firefox 中,模型不会更新,直到您离开下拉列表,所以新的输入框出现得太晚了;焦点转到下一个。
根据 this discussion on the AngularJS GitHub, this might be related to when the browser fires the onchange event, and Firefox may be doing it properly according to spec. Regardless, I want to deal with these cases gracefully and in a generalizable way. Any solution also must account for SHIFT+TAB, which takes you backwards in the DOM through the focusable elements. In another Whosebug thread,有人给出了一个指令示例,该指令将在元素出现时立即将焦点设置到该元素。我会尝试在有时可见的下一个输入中添加类似的内容并报告回来。
我知道这个解决方案是 jQuery 解决方案,但它代表了一种您可以在 Angular.js 中实现的技术。
解决方法是在 UI 更新时用 "busy popup" 拦截焦点,然后将焦点移动到 next/previous 字段。
一个示例实现是这个页面:http://dylanb.github.io/a11yvalid/jqvalid9.html
如果您输入的出生日期小于 18 岁,它将模拟服务器调用(超时),然后使用监护人信息的新字段更新表单。
代码在此存储库中 https://github.com/dylanb/a11yvalid
可在此处找到错误实施的示例:http://dylanb.github.io/a11yvalid/jqvalid8.html
[太平洋标准时间下午 1 点:编辑为在*任何* keydown 事件上触发 change(),而不仅仅是 UP 或 DOWN,因为用户还可以通过按字符键来浏览选项,例如字母。]
[太平洋标准时间下午 2 点:请注意,这使用 JQuery 来触发处理程序。如果你有Angular with JQLite,你可以使用element.triggerHandler('change')
.]
我经历了几次 'focus me when I show up (if these conditions are met) (after this amount of time)' 的迭代,但所有这些迭代都在一种或另一种浏览器中提供了糟糕的屏幕阅读器体验。
所以我最终采用了显而易见的方法:我将一个 keydown 处理程序附加到 select
,当按下任何键时它会触发 change() 事件。
.directive("updateOnKeydown", ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
// updates model when a key is pressed; now, when a keyboard user
// tabs out of the dropdown after using the arrows/letter keys to make
// a selection, the DOM is up-to-date and focus will be set to the
// correct element
element.bind('keydown', function (evt) {
evt = (!evt) ? window.event : evt;
var keyCode = evt.keyCode || evt.charCode;
if (keyCode) { // do we want to exclude anything?
if (!scope.$$phase) {
$timeout(function () {
$(element).change();
});
}
else {
console.log('in the middle of something!');
}
}
});
}
}
}])
和...
<select ng-model="needAnotherInput" update-on-keydown>
它在我需要的任何地方都运行良好,但如果您愿意,建议进行一些调整。 (我应该从 keydown 处理程序返回一些东西吗?如果它处于摘要循环的中间,我应该再试一次吗?)