协慌网

登录 贡献 社区

如何设置焦点在输入字段?

在 AngularJS 中设置焦点到输入字段的 “角度方式” 是什么?

更具体的要求:

  1. 打开模态时 ,将焦点设置在此模态内的预定义<input>
  2. 每次<input>变得可见(例如,通过单击某个按钮),将焦点设置在它上面。

我试图autofocus 实现第一个要求 ,但这只适用于第一次打开 Modal 时,并且仅在某些浏览器中(例如在 Firefox 中不起作用)。

任何帮助将不胜感激。

答案

  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”。每次 变得可见(例如,通过单击某个按钮),将焦点设置在它上面。

创建一个基本上与上面相似的指令。观察一些 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


2013 年 7 月更新 :我见过一些人使用我原来的隔离范围指令,然后遇到嵌入式输入字段的问题(即模态中的输入字段)。没有新范围(或可能是新的子范围)的指令应该可以缓解一些痛苦。所以上面我更新了不使用隔离范围的答案。以下是原始答案:

原始答案为 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” 进行比较,因为 @始终会生成一个字符串。

(编辑:我在此解释下添加了更新的解决方案)

Mark Rajcok 是男人...... 他的答案是一个有效的答案,但它一个缺陷(对不起 Mark)......

... 尝试使用布尔值来聚焦输入,然后模糊输入,然后尝试使用它再次聚焦输入。除非您将布尔值重置为 false,然后将 $ digest 重置,然后将其重置为 true,否则它将无效。即使您在表达式中使用字符串比较,您也将被迫将字符串更改为其他内容,$ digest,然后将其更改回来。 (这已通过模糊事件处理程序解决。)

所以我建议这个替代解决方案:

使用事件,Angular 的遗忘功能。

毕竟 JavaScript 喜欢事件。事件本质上是松耦合的,甚至更好,你避免在 $ digest 中添加另一个 $ watch。

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 开发人员,我们非常努力地将范围形状的钉子敲入事件形状孔。

这是最好的解决方案吗?我不知道。这是一个解决方案。


更新方案

在 @ 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 渲染中的东西,即使它是零,它至少等待它 - 这种方式适用于模态和其他什么