Как установить фокус на поле ввода?

каков "угловой способ" установить фокус на поле ввода в AngularJS?

особые требования:

  1. когда модальные открывается, установите фокус на предопределенном <input> внутри этого модального.
  2. каждый раз <input> становится видимым (например, нажав какую-то кнопку), установите фокус на нем.

я попытался выполнить первое требование С autofocus, но это работает только тогда, когда модальные открыт в первый раз, и только в некоторых браузерах (например, в Firefox это не работает).

любая помощь будет оценили.

30 ответов


  1. когда модальный открыт, установите фокус на предопределенном внутри этого модального.

определите директиву и попросите $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));
            });
        }
    };
}]);

Plunker

кажется, что $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;
          //});
        }
      });
    }
  };
});

Plunker


обновление 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(); 
          });
        }
      });
    }
  };
});

Plunker.

оригинальный ответ на 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;
          //});
        }
      });
    }
  };
});

Plunker.

поскольку нам нужно сбросить свойство 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, даже если он равен нулю, он, по крайней мере, ждет этого - таким образом, это работает в модалах и еще много чего


HTML имеет атрибут autofocus.

<input type="text" name="fname" autofocus>

http://www.w3schools.com/tags/att_input_autofocus.asp


вы также можете использовать функциональность 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


вот мое оригинальное решение:

plunker

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>

вот скрипку:

http://fiddle.jshell.net/ubenzer/9FSL4/8/


для тех, кто использует 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();
        });
    }]);