Как установить фокус на поле ввода?
Что такое "угловой способ", чтобы установить фокус на поле ввода в AngularJS?
Более конкретные требования:
- Когда модальный открывается, установите фокус на предопределенный
<input>
внутри этого модала. - Каждый раз
<input>
становится видимым (например, при нажатии какой-либо кнопки), установите фокус на нем.
Я пытался выполнить первое требование с autofocus
, но это работает только тогда, когда модал открывается в первый раз, и только в определенных браузерах (например, в Firefox он не работает).
Любая помощь будет оценена.
34 ответа
- Когда модальное окно открыто, установите фокус на предварительно определенный внутри этого модального окна.
Определите директиву и задайте ей $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.' Каждый раз, когда становится видимым (например, нажатием какой-либо кнопки), установите фокус на него.
Создайте директиву, по сути, похожую на приведенную выше. Следите за некоторым свойством контекста, и когда оно станет истинным (установите его в вашем обработчике ng-click), выполните element[0].focus()
, В зависимости от вашего варианта использования, вам может понадобиться или не потребоваться тайм-аут $ для этого:
<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", поскольку @ всегда приводит к строке.
(РЕДАКТИРОВАТЬ: я добавил обновленное решение ниже этого объяснения)
Марк Райкок - человек... и его ответ - правильный ответ, но у него был недостаток (извините, Марк)...
... Попробуйте использовать логическое значение, чтобы сфокусироваться на входе, затем размыть вход, затем попробуйте использовать его, чтобы снова сфокусировать ввод. Это не сработает, если вы не сбросите логическое значение на 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 в отверстия формы событий.
Это лучшее решение? Я не знаю. Это решение.
Обновленное решение
После комментария @ShimonRachlenko ниже я немного изменил свой метод. Теперь я использую комбинацию службы и директивы, которая обрабатывает событие "за сценой":
Помимо этого, это тот же принцип, изложенный выше.
использование
<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>
Мы используем тайм-аут, чтобы позволить объектам в домене визуализироваться, даже если он равен нулю, он, по крайней мере, ждет этого - таким образом, это работает в модальных моделях и еще много чего.
HTML имеет атрибут autofocus
,
<input type="text" name="fname" autofocus>
Вы также можете использовать функциональность jqlite, встроенную в angular.
angular.element('.selector').trigger('focus');
Это хорошо работает и угловой способ фокусировки ввода управления
angular.element('#elementId').focus()
Это хотя и не чисто угловой способ выполнения задачи, но синтаксис следует угловому стилю. Jquery играет косвенную роль и напрямую обращается к DOM, используя Angular (jQLite => JQuery Light).
При необходимости этот код можно легко поместить в простую угловую директиву, где элемент доступен напрямую.
Я не думаю, что $ timeout - это хороший способ сфокусировать элемент на создании. Вот метод, использующий встроенную угловую функциональность, вырытую из темных глубин угловых документов. Обратите внимание, как атрибут "ссылка" можно разделить на "pre" и "post" для функций pre-link и post-link.
// 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 />
Вот мое оригинальное решение:
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 или $ здесь.
Я недавно написал директиву о двухстороннем связывании фокуса, как и модель.
Вы можете использовать директиву 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/
Вы можете подключиться к opened
обещание модального экземпляра:
modalInstance.opened.then(function() {
$timeout(function() {
angular.element('#title_input').trigger('focus');
});
});
modalInstance.result.then(function ( etc...
Я нашел полезным использовать общее выражение. Таким образом, вы можете делать такие вещи, как автоматическое перемещение фокуса, когда введенный текст действителен
<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
}
}
});
}
}
};
});
Не воскрешать зомби и не вставлять мою собственную директиву (хорошо, это именно то, что я делаю):
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
};
}
})();
Вместо создания своей собственной директивы можно просто использовать функции javascript для достижения цели.
Вот пример.
В HTML-файл:
<input type="text" id="myInputId" />
В файле javascript, например, в контроллере, где вы хотите активировать фокус:
document.getElementById("myInputId").focus();
Во-первых, официальный способ сосредоточиться на дорожной карте для 1.1. Между тем, вы можете написать директиву для реализации настроек фокуса.
Во-вторых, для фокусировки на элементе после того, как он стал видимым, в настоящее время требуется обходной путь. Просто задержите вызов элемента focus() с помощью $timeout
,
Поскольку для фокусировки, размытия и выделения существует та же проблема controller-modizes-DOM, я предлагаю иметь ng-target
директива:
<input type="text" x-ng-model="form.color" x-ng-target="form.colorTarget">
<button class="btn" x-ng-click="form.colorTarget.focus()">do focus</button>
Угловая тема здесь: http://goo.gl/ipsx4, а более подробная информация опубликована здесь: http://goo.gl/4rdZa
Следующая директива создаст .focus()
функция внутри вашего контроллера, как указано вашим ng-target
приписывать. (Это создает .blur()
и .select()
тоже.) Демо: http://jsfiddle.net/bseib/WUcQX/
Если вы просто хотели простой фокус, которым управлял нг-клик.
Html:
<input ut-focus="focusTigger">
<button ng-click="focusTrigger=!focusTrigger" ng-init="focusTrigger=false"></button>
Директива:
'use strict'
angular.module('focus',['ng'])
.directive('utFocus',function($timeout){
return {
link:function(scope,elem,attr){
var focusTarget = attr['utFocus'];
scope.$watch(focusTarget,function(value){
$timeout(function(){
elem[0].focus();
});
});
}
}
});
Простой, который хорошо работает с модалами:
.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 />
У Марка и Блеша прекрасные ответы; однако у Марка есть недостаток, на который указывает Блеш (помимо того, что он сложен для реализации), и я чувствую, что ответ Блеша имеет семантическую ошибку при создании службы, которая конкретно направляет запрос фокуса на внешний интерфейс, когда на самом деле все, что ему нужно, - это способ отложить событие, пока все директивы не будут прослушаны.
Итак, вот то, что я в итоге сделал, которое во многом зависит от ответа Blesh, но разделяет семантику события контроллера и службы "после загрузки".
Это позволяет легко перехватывать событие контроллера для других вещей, помимо простой фокусировки на конкретном элементе, а также позволяет нести служебную нагрузку функции "после загрузки", только если она необходима, чего во многих случаях может не быть.
использование
<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();
-
NB. В зависимости от контекста вам, возможно, придется включить функцию тайм-аута.
NB²: при использовании controllerAs
это почти то же самое. Просто замени name="myForm"
с name="vm.myForm"
и в JS, vm.myForm.myText.$$element.focus();
,
Вы можете просто создать директиву, которая заставляет сосредоточиться на украшенном элементе на 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.
Наверное, самое простое решение по возрасту ES6.
Добавление следующей директивы liner делает HTML-атрибут autofocus эффективным в Angular.js.
.directive('autofocus', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())}))
Теперь вы можете просто использовать синтаксис автофокуса HTML5, например:
<input type="text" autofocus>
Я редактирую директиву 'Mark Rajcok' focusMe для работы с несколькими фокусами в одном элементе.
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);
}
});
}
};
});
Если вы используете modalInstance и у вас есть объект, вы можете использовать "затем" для выполнения действий после открытия модального. Если вы не используете modalInstance и жестко запрограммированы, чтобы открыть модал, вы можете использовать событие. Тайм-аут $ не является хорошим решением.
Вы можете сделать (Bootstrap3):
$("#" + modalId).on("shown.bs.modal", function() {
angular.element("[name='name']").focus();
});
В modalInstance вы можете посмотреть на библиотеку, как выполнить код после открытия модального.
Не используйте $timeout, как это, $timeout может быть 0, 1, 10, 30, 50, 200 или больше, это будет зависеть от клиентского компьютера и процесса открытия модального режима.
Не используйте $timeout, пусть метод скажет вам, когда вы можете сосредоточиться;)
Я надеюсь, что это поможет!:)
Просто новичок здесь, но я был в состоянии заставить его работать в ui.bootstrap.modal с этой директивой:
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.open я использовал следующие элементы, чтобы указать элемент, на который следует поместить фокус:
var d = $modal.open({
controller : function($scope, $modalInstance) {
...
$scope.idToFocus = "cancelaAteste";
}
...
});
на шаблоне у меня есть это:
<input id="myInputId" focus />
Весь предыдущий ответ не работает, если нужный элемент фокуса вставлен в шаблон директивы. Следующая директива подходит как для простого элемента, так и для вставленного элемента директивы (я написал ее в машинописи). он принимает селектор для внутреннего фокусируемого элемента. если вам просто нужно сфокусировать элемент self - не отправляйте параметр селектора в директиву:
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 вместо "ввода"
Следующая директива сработала для меня. Используйте тот же атрибут html автофокуса для ввода.
.directive('autofocus', [function () {
return {
require : 'ngModel',
restrict: 'A',
link: function (scope, element, attrs) {
element.focus();
}
};
}])
Если вы хотите установить фокус на конкретный элемент, вы можете использовать подход ниже.
Создать сервис под названием
focus
,angular.module('application') .factory('focus', function ($timeout, $window) { return function (id) { $timeout(function () { var element = $window.document.getElementById(id); if (element) element.focus(); }); }; });
Введите его в контроллер, откуда вы хотите позвонить.
Позвоните в эту службу.
Это легко.. попробуйте это
HTML
<select id="ddl00">
<option>"test 01"</option>
</select>
Javascript
document.getElementById("ddl00").focus();
Я хочу внести свой вклад в эту дискуссию после того, как искал лучшее решение и не нашел его, а вместо этого создал его.
Критерии: 1. Решение должно быть независимым от области действия родительского контроллера для увеличения возможности повторного использования. 2. Избегайте использования $watch для отслеживания некоторых условий, это является медленным, увеличивает размер цикла дайджеста и усложняет тестирование. 3. Избегайте $timeout или $scope.$ Apply() для запуска дайджест-цикла. 4. Элемент ввода присутствует внутри элемента, в котором используется директива open.
Это решение мне понравилось больше всего:
Директива:
.directive('focusInput', [ function () {
return {
scope: {},
restrict: 'A',
compile: function(elem, attr) {
elem.bind('click', function() {
elem.find('input').focus();
});
}
};
}]);
Html:
<div focus-input>
<input/>
</div>
Я надеюсь, что это поможет кому-то там!
Просто добавляю кофе.
app.directive 'ngAltFocus', ->
restrict: 'A'
scope: ngAltFocus: '='
link: (scope, el, attrs) ->
scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Я думаю, что директива не нужна. Используйте HTML id и атрибуты класса, чтобы выбрать необходимый элемент, и попросите службу использовать document.getElementById или document.querySelector для применения фокуса (или эквивалентов jQuery).
Разметка - это стандартные HTML/ угловые директивы с добавленными идентификаторами / классами для выбора.
<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() для запуска цикла дайджеста.