Как установить фокус на поле ввода?
каков "угловой способ" установить фокус на поле ввода в AngularJS?
особые требования:
- когда модальные открывается, установите фокус на предопределенном
<input>
внутри этого модального. - каждый раз
<input>
становится видимым (например, нажав какую-то кнопку), установите фокус на нем.
я попытался выполнить первое требование С autofocus
, но это работает только тогда, когда модальные открыт в первый раз, и только в некоторых браузерах (например, в Firefox это не работает).
любая помощь будет оценили.
30 ответов
- когда модальный открыт, установите фокус на предопределенном внутри этого модального.
определите директиву и попросите $watch свойство / триггер, чтобы он знал, когда фокусировать элемент:
Name: <input type="text" focus-me="shouldBeOpen">
app.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {
return {
//scope: true, // optionally create a child scope
link: function (scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function (value) {
console.log('value=', value);
if (value === true) {
$timeout(function () {
element[0].focus();
});
}
});
// to address @blesh's comment, set attribute value to 'false'
// on blur event:
element.bind('blur', function () {
console.log('blur');
scope.$apply(model.assign(scope, false));
});
}
};
}]);
кажется, что $timeout необходим, чтобы дать модальное время для рендеринга.
'2.'Everytime становится видимым (например, нажав какую-то кнопку), установите фокус на нем.
создайте директиву, по существу похожую на приведенную выше. Просмотрите некоторое свойство scope, и когда оно станет true (установите его в обработчике ng-click), выполните element[0].focus()
. В зависимости от вашего варианта использования вам может понадобиться или не понадобиться $timeout для этого:
<button class="btn" ng-click="showForm=true; focusInput=true">show form and
focus input</button>
<div ng-show="showForm">
<input type="text" ng-model="myInput" focus-me="focusInput"> {{ myInput }}
<button class="btn" ng-click="showForm=false">hide form</button>
</div>
app.directive('focusMe', function($timeout) {
return {
link: function(scope, element, attrs) {
scope.$watch(attrs.focusMe, function(value) {
if(value === true) {
console.log('value=',value);
//$timeout(function() {
element[0].focus();
scope[attrs.focusMe] = false;
//});
}
});
}
};
});
обновление 7/2013: я видел, как несколько человек использовали мой оригинальный изолят директивы области, а затем возникают проблемы со встроенными полями ввода (т. е. полем ввода в модальном режиме). Директива без новой области (или, возможно, новой дочерней области) должна облегчить некоторую боль. Поэтому выше я обновил ответ, чтобы не использовать области изоляции. Ниже приведен оригинальный ответ:
оригинальный ответ на 1., используя область изолирования:
Name: <input type="text" focus-me="{{shouldBeOpen}}">
app.directive('focusMe', function($timeout) {
return {
scope: { trigger: '@focusMe' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
оригинальный ответ на 2. использование изолировать область:
<button class="btn" ng-click="showForm=true; focusInput=true">show form and
focus input</button>
<div ng-show="showForm">
<input type="text" focus-me="focusInput">
<button class="btn" ng-click="showForm=false">hide form</button>
</div>
app.directive('focusMe', function($timeout) {
return {
scope: { trigger: '=focusMe' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === true) {
//console.log('trigger',value);
//$timeout(function() {
element[0].focus();
scope.trigger = false;
//});
}
});
}
};
});
поскольку нам нужно сбросить свойство trigger/focusInput в директиве, '=' используется для двусторонней привязки данных. В первой директиве было достаточно"@". Также обратите внимание, что при использовании '@' мы сравниваем значение триггера с "true", так как @ всегда приводит к строке.
(EDIT: я добавил обновленное Решение ниже этого объяснения)
Марк Rajcok человек... и его ответ-Правильный ответ, но он и был дефект (извините знак)...
...Попробуйте использовать логическое значение для фокусировки на входе, затем размыть вход, а затем снова сфокусировать вход. Он не будет работать, если вы не сбросите логическое значение false, затем $digest, а затем сбросите его обратно в true. Даже если вы используете сравнение строк в своем выражении, вы будете вынуждены изменить строку на что-то другое, $digest, а затем изменить ее обратно. (это было рассмотрено с помощью обработчика событий размытия.)
поэтому я предлагаю это альтернативное решение:
используйте событие, забытую функцию Angular.
JavaScript любит события в конце концов. События по своей сути слабо связаны, и даже лучше, вы избегаете добавления еще одного $watch в свой $digest.
app.directive('focusOn', function() {
return function(scope, elem, attr) {
scope.$on(attr.focusOn, function(e) {
elem[0].focus();
});
};
});
так что теперь вы можете использовать его как это:
<input type="text" focus-on="newItemAdded" />
а затем в любом месте вашего приложения...
$scope.addNewItem = function () {
/* stuff here to add a new item... */
$scope.$broadcast('newItemAdded');
};
это потрясающе, потому что вы можете делать все виды вещей с чем-то вроде этого. Во-первых, вы можете связать события, которые уже существуют. С другой стороны, вы начинаете делать что-то умное, публикуя события, на которые могут подписаться другие части вашего приложения.
во всяком случае, этот тип вещей кричит мне "событие". Я думаю, как разработчики Angular мы стараемся действительно трудно забить $ scope shaped колышки в отверстия формы события.
это лучшее решение? Не знаю. Это a решение.
Обновленное Решение
после комментария @ShimonRachlenko ниже я немного изменил свой способ сделать это. Теперь я использую комбинацию службы и директивы, которая обрабатывает событие "за кулисами":
кроме этого, это тот же принцип, изложенный выше.
вот быстрая демонстрация Plunk
использование
<input type="text" focus-on="focusMe"/>
app.controller('MyCtrl', function($scope, focus) {
focus('focusMe');
});
источник
app.directive('focusOn', function() {
return function(scope, elem, attr) {
scope.$on('focusOn', function(e, name) {
if(name === attr.focusOn) {
elem[0].focus();
}
});
};
});
app.factory('focus', function ($rootScope, $timeout) {
return function(name) {
$timeout(function (){
$rootScope.$broadcast('focusOn', name);
});
}
});
Я нашел некоторые из других ответов слишком сложными, когда все, что вам действительно нужно, это
app.directive('autoFocus', function($timeout) {
return {
restrict: 'AC',
link: function(_scope, _element) {
$timeout(function(){
_element[0].focus();
}, 0);
}
};
});
использование
<input name="theInput" auto-focus>
мы используем тайм-аут, чтобы позволить вещам в рендере dom, даже если он равен нулю, он, по крайней мере, ждет этого - таким образом, это работает в модалах и еще много чего
вы также можете использовать функциональность jqlite, встроенную в angular.
angular.element('.selector').trigger('focus');
Это хорошо работает, и угловой способ управления фокусом ввода
angular.element('#elementId').focus()
это хотя и не чистый угловой способ выполнения задачи, но синтаксис следует угловому стилю. Jquery играет роль косвенно и напрямую обращается к DOM с помощью Angular (Jqlite => jQuery Light).
Если требуется, этот код можно легко поместить внутри простой угловой директивы, где элемент доступен напрямую.
я не думаю, что $ timeout-хороший способ сосредоточить элемент на создании. Вот метод, использующий встроенную угловую функциональность, выкопанную из мутных глубин угловых документов. Обратите внимание, как атрибут" link "можно разделить на" pre "и" post " для функций pre-link и post-link.
рабочий пример: http://plnkr.co/edit/Fj59GB
// this is the directive you add to any element you want to highlight after creation
Guest.directive('autoFocus', function() {
return {
link: {
pre: function preLink(scope, element, attr) {
console.debug('prelink called');
// this fails since the element hasn't rendered
//element[0].focus();
},
post: function postLink(scope, element, attr) {
console.debug('postlink called');
// this succeeds since the element has been rendered
element[0].focus();
}
}
}
});
<input value="hello" />
<!-- this input automatically gets focus on creation -->
<input value="world" auto-focus />
Полная Директива AngularJS Docs: https://docs.angularjs.org/api/ng/service / $compile
вот мое оригинальное решение:
var app = angular.module('plunker', []);
app.directive('autoFocus', function($timeout) {
return {
link: function (scope, element, attrs) {
attrs.$observe("autoFocus", function(newValue){
if (newValue === "true")
$timeout(function(){element[0].focus()});
});
}
};
});
и HTML:
<button ng-click="isVisible = !isVisible">Toggle input</button>
<input ng-show="isVisible" auto-focus="{{ isVisible }}" value="auto-focus on" />
принцип действия:
он фокусирует вход, поскольку он становится видимым с ng-show. Здесь нет использования $ watch или $on.
Я написал двустороннюю директиву привязки фокуса, как и модель недавно.
вы используете директиву focus следующим образом:
<input focus="someFocusVariable">
Если вы сделаете переменную области someFocusVariable true в любом месте вашего контроллера, вход получил фокус. И если вы" размыли " свой ввод, someFocusVariable имеет значение false. Это похоже на первый ответ Марка Раджкока, но с двусторонней привязкой.
здесь директива:
function Ctrl($scope) {
$scope.model = "ahaha"
$scope.someFocusVariable = true; // If you want to focus initially, set this to true. Else you don't need to define this at all.
}
angular.module('experiement', [])
.directive('focus', function($timeout, $parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(attrs.focus, function(newValue, oldValue) {
if (newValue) { element[0].focus(); }
});
element.bind("blur", function(e) {
$timeout(function() {
scope.$apply(attrs.focus + "=false");
}, 0);
});
element.bind("focus", function(e) {
$timeout(function() {
scope.$apply(attrs.focus + "=true");
}, 0);
})
}
}
});
использование:
<div ng-app="experiement">
<div ng-controller="Ctrl">
An Input: <input ng-model="model" focus="someFocusVariable">
<hr>
<div ng-click="someFocusVariable=true">Focus!</div>
<pre>someFocusVariable: {{ someFocusVariable }}</pre>
<pre>content: {{ model }}</pre>
</div>
</div>
вот скрипку:
для тех, кто использует Angular с плагином Bootstrap:
http://angular-ui.github.io/bootstrap/#/modal
вы можете подключиться к opened
обещание модального экземпляра:
modalInstance.opened.then(function() {
$timeout(function() {
angular.element('#title_input').trigger('focus');
});
});
modalInstance.result.then(function ( etc...
вместо создания собственной директивы можно просто использовать функции javascript для выполнения фокуса.
вот пример.
в файле html:
<input type="text" id="myInputId" />
в файле javascript, например, в контроллере, где вы хотите активировать фокус:
document.getElementById("myInputId").focus();
не воскрешать зомби или подключать мою собственную директиву (ок, это именно то, что я делаю):
https://github.com/hiebj/ng-focus-if
http://plnkr.co/edit/MJS3zRk079Mu72o5A9l6?p=preview
<input focus-if />
(function() {
'use strict';
angular
.module('focus-if', [])
.directive('focusIf', focusIf);
function focusIf($timeout) {
function link($scope, $element, $attrs) {
var dom = $element[0];
if ($attrs.focusIf) {
$scope.$watch($attrs.focusIf, focus);
} else {
focus(true);
}
function focus(condition) {
if (condition) {
$timeout(function() {
dom.focus();
}, $scope.$eval($attrs.focusDelay) || 0);
}
}
}
return {
restrict: 'A',
link: link
};
}
})();
во-первых, официальный способ сделать акцент на план 1.1. Между тем, вы можете написать директиву для реализации настройки фокуса.
во-вторых, чтобы установить фокус на элемент после того, как он стал видимым, в настоящее время требуется обходной путь. Просто отложите вызов элемента focus () с помощью $timeout
.
потому что та же проблема контроллера-модифицирует-DOM существует для фокусировки, размытия и выбора, я предлагаю иметь . (Он создает .blur()
и .select()
тоже.) Demo:http://jsfiddle.net/bseib/WUcQX/
Я счел полезным использовать общее выражение. Таким образом, вы можете делать такие вещи, как автоматическое перемещение фокуса, когда входной текст действителен
<button type="button" moo-focus-expression="form.phone.$valid">
или автоматически фокусироваться, когда пользователь завершает поле фиксированной длины
<button type="submit" moo-focus-expression="smsconfirm.length == 6">
и после нагрузки
<input type="text" moo-focus-expression="true">
код директивы:
.directive('mooFocusExpression', function ($timeout) {
return {
restrict: 'A',
link: {
post: function postLink(scope, element, attrs) {
scope.$watch(attrs.mooFocusExpression, function (value) {
if (attrs.mooFocusExpression) {
if (scope.$eval(attrs.mooFocusExpression)) {
$timeout(function () {
element[0].focus();
}, 100); //need some delay to work with ng-disabled
}
}
});
}
}
};
});
Если вы просто хотели простой фокус, который контролировался ng-click.
HTML-код:
<input ut-focus="focusTigger">
<button ng-click="focusTrigger=!focusTrigger" ng-init="focusTrigger=false"></button>
просто тот, который хорошо работает с глагола:
.directive('focusMeNow', ['$timeout', function ($timeout)
{
return {
restrict: 'A',
link: function (scope, element, attrs)
{
$timeout(function ()
{
element[0].focus();
});
}
};
}])
пример
<input ng-model="your.value" focus-me-now />
вы можете просто создать директиву, которая заставляет сосредоточиться на украшенном элементе на postLinking:
angular.module('directives')
.directive('autoFocus', function() {
return {
restrict: 'AC',
link: function(_scope, _element) {
_element[0].focus();
}
};
});
затем в вашем html:
<input type="text" name="first" auto-focus/> <!-- this will get the focus -->
<input type="text" name="second"/>
это будет работать для модалов и ng-if переключаемых элементов, а не для ng-show, так как postLinking происходит только при обработке HTML.
У Марка и Блеша есть отличные ответы; однако у Марка есть недостаток, на который указывает Блеш (помимо того, что он сложный для реализации), и я чувствую, что ответ Блеша имеет семантическую ошибку в создании службы, которая конкретно связана с отправкой запроса фокуса на интерфейс, когда на самом деле все, что ему нужно, это способ отложить событие, пока все директивы не будут слушать.
Итак, вот что я закончил делать, что ворует много из ответа Блеша, но сохраняет семантику контроллера событие и Служба" после загрузки " разделяются.
Это позволяет событию контроллера легко подключаться к вещам, отличным от фокусировки определенного элемента, а также позволяет нести накладные расходы функции "после загрузки" только в том случае, если это необходимо, чего может не быть во многих случаях.
использование
<input type="text" focus-on="controllerEvent"/>
app.controller('MyCtrl', function($scope, afterLoad) {
function notifyControllerEvent() {
$scope.$broadcast('controllerEvent');
}
afterLoad(notifyControllerEvent);
});
источник
app.directive('focusOn', function() {
return function(scope, elem, attr) {
scope.$on(attr.focusOn, function(e, name) {
elem[0].focus();
});
};
});
app.factory('afterLoad', function ($rootScope, $timeout) {
return function(func) {
$timeout(func);
}
});
это также можно использовать ngModelController
. Работа с 1.6+ (не знаю с более старыми версиями).
HTML-код
<form name="myForm">
<input type="text" name="myText" ng-model="myText">
</form>
JS
$scope.myForm.myText.$$element.focus();
--
N. B.: В зависимости от контекста вам, возможно, придется обернуть функцию тайм-аута.
N. B. 2: при использовании controllerAs
, это почти то же самое. Просто замените name="myForm"
С name="vm.myForm"
и в JS, vm.myForm.myText.$$element.focus();
.
вероятно, самое простое решение для возраста ES6.
добавление после одной директивы liner делает атрибут HTML "автофокус" эффективным на угловом.js.
.directive('autofocus', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())}))
теперь вы можете просто использовать синтаксис автофокусировки HTML5, например:
<input type="text" autofocus>
просто новичок здесь, но я был в состоянии заставить его работать в ui.загрузчик.модальный С этой директивой:
directives.directive('focus', function($timeout) {
return {
link : function(scope, element) {
scope.$watch('idToFocus', function(value) {
if (value === element[0].id) {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
и в $ modal.открытый метод я использовал folowing, чтобы указать элемент, где фокус должен быть помещен:
var d = $modal.open({
controller : function($scope, $modalInstance) {
...
$scope.idToFocus = "cancelaAteste";
}
...
});
в шаблоне у меня есть это:
<input id="myInputId" focus />
я редактирую директиву focusMe "Mark Rajcok" для работы с несколькими фокусами в одном элементе.
HTML:
<input focus-me="myInputFocus" type="text">
в контроллере AngularJs:
$scope.myInputFocus= true;
Директива AngulaJS:
app.directive('focusMe', function ($timeout, $parse) {
return {
link: function (scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function (value) {
if (value === true) {
$timeout(function () {
scope.$apply(model.assign(scope, false));
element[0].focus();
}, 30);
}
});
}
};
});
следующая директива сделала трюк для меня. Используйте тот же атрибут html автофокусировки для ввода.
.directive('autofocus', [function () {
return {
require : 'ngModel',
restrict: 'A',
link: function (scope, element, attrs) {
element.focus();
}
};
}])
Если вы используете modalInstance и имеете объект, вы можете использовать "затем"для выполнения действий после открытия модального. Если вы не используете modalInstance и жестко закодированы для открытия модального, вы можете использовать событие. В $timeout не является хорошим решением.
вы можете сделать (Bootstrap3):
$("#" + modalId).on("shown.bs.modal", function() {
angular.element("[name='name']").focus();
});
в modalInstance вы можете посмотреть библиотеку, как выполнить код после открытия модального.
Не используйте $ timeout как это, $ timeout может быть 0, 1, 10, 30, 50, 200 или больше это будет зависеть от клиентского компьютера,а также от процесса открытия модального.
Не используйте $ timeout пусть метод скажет вам, когда вы можете сосредоточиться;)
Я надеюсь, что это поможет! :)
весь предыдущий ответ не работает, если нужный элемент фокуса вводится в шаблон директивы. Следующая директива подходит как к простому элементу, так и к элементу, введенному директивой (я написал ее в машинопись). он принимает селектор для внутреннего focusable элемента. если вам просто нужно сфокусировать элемент self - не отправляйте параметр selector в директиву:
module APP.Directives {
export class FocusOnLoadDirective implements ng.IDirective {
priority = 0;
restrict = 'A';
constructor(private $interval:any, private $timeout:any) {
}
link = (scope:ng.IScope, element:JQuery, attrs:any) => {
var _self = this;
var intervalId:number = 0;
var clearInterval = function () {
if (intervalId != 0) {
_self.$interval.cancel(intervalId);
intervalId = 0;
}
};
_self.$timeout(function(){
intervalId = _self.$interval(function () {
let focusableElement = null;
if (attrs.focusOnLoad != '') {
focusableElement = element.find(attrs.focusOnLoad);
}
else {
focusableElement = element;
}
console.debug('focusOnLoad directive: trying to focus');
focusableElement.focus();
if (document.activeElement === focusableElement[0]) {
clearInterval();
}
}, 100);
scope.$on('$destroy', function () {
// Make sure that the interval is destroyed too
clearInterval();
});
});
};
public static factory = ():ng.IDirectiveFactory => {
let directive = ($interval:any, $timeout:any) => new FocusOnLoadDirective($interval, $timeout);
directive.$inject = ['$interval', '$timeout'];
return directive;
};
}
angular.module('common').directive('focusOnLoad', FocusOnLoadDirective.factory());
}
пример использования для простых элемент:
<button tabindex="0" focus-on-load />
пример использования для внутреннего элемента (обычно для динамического инжектируемого элемента, такого как директива с шаблоном):
<my-directive focus-on-load="input" />
вы можете использовать любой селектор jQuery вместо "input"
это легко.. попробуйте это
HTML-код
<select id="ddl00">
<option>"test 01"</option>
</select>
в JavaScript
document.getElementById("ddl00").focus();
Я хочу внести свой вклад в эту дискуссию после поиска лучшего решения и не найти его, а вместо этого создать его.
критерии: 1. Решение должно быть независимым от области родительского контроллера для повышения удобства использования. 2. Избегайте использования $ watch для мониторинга некоторых условий, это медленно, увеличивает размер цикла дайджеста и затрудняет тестирование. 3. Избегайте $ timeout или $scope.$apply () для запуска цикла дайджеста. 4. Элемент input присутствует внутри элемент, в котором используется директива open.
это решение мне понравилось больше всего:
Я думаю, что директива не нужна. Используйте HTML id и атрибуты класса, чтобы выбрать необходимый элемент и документ использования службы.метода getElementById или document.querySelector для применения фокуса (или эквивалентов jQuery).
разметка-это стандартные HTML / угловые директивы с добавлением id / классов для выбора
<input id="myInput" type="text" ng-model="myInputModel" />
контроллер транслирует событие
$scope.$emit('ui:focus', '#myInput');
в службе UI используется querySelector - если есть несколько совпадений (скажем, из-за класса), это будет только верните первое
$rootScope.$on('ui:focus', function($event, selector){
var elem = document.querySelector(selector);
if (elem) {
elem.focus();
}
});
вы можете использовать $ timeout (), чтобы заставить цикл дайджеста
просто добавляю немного кофе.
app.directive 'ngAltFocus', ->
restrict: 'A'
scope: ngAltFocus: '='
link: (scope, el, attrs) ->
scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Не уверен, что полагаться на тайм-аут-хорошая идея, но это работает для ng-repeat, потому что этот код запускается после обновления angularjs DOM, поэтому вы убедитесь, что все объекты есть:
myApp.directive('onLastRepeat', [function () {
return function (scope, element, attrs) {
if (scope.$last) setTimeout(function () {
scope.$emit('onRepeatLast', element, attrs);
}, 1);
};
}]);
//controller for grid
myApp.controller('SimpleController', ['$scope', '$timeout', '$http', function ($scope, $timeout, $http)
{
var newItemRemoved = false;
var requiredAlert = false;
//this event fires up when angular updates the dom for the last item
//it's observed, so here, we stop the progress bar
$scope.$on('onRepeatLast', function (scope, element, attrs) {
//$scope.complete();
console.log('done done!');
$("#txtFirstName").focus();
});
}]);