774 Stimmen

Wie setzt man den Fokus auf ein Eingabefeld?

Was ist der 'Angular Way', um den Fokus auf ein Eingabefeld in AngularJS zu setzen?

Weitere spezifische Anforderungen:

  1. Wenn ein Modal geöffnet wird, den Fokus auf ein vordefiniertes innerhalb dieses Modals setzen.
  2. Jedes Mal, wenn ein sichtbar wird (z. B. durch Klicken auf eine Schaltfläche), den Fokus darauf setzen.

Ich habe versucht, die erste Anforderung zu erreichen mit autofocus, aber das funktioniert nur, wenn das Modal zum ersten Mal geöffnet wird, und nur in bestimmten Browsern (z. B. in Firefox funktioniert es nicht).

591voto

Mark Rajcok Punkte 356006
  1. Wenn ein Modal geöffnet wird, legen Sie den Fokus auf ein vordefiniertes innerhalb dieses Modals.

Definieren Sie eine Direktive und lassen Sie sie eine Eigenschaft/Trigger $watchen, damit sie weiß, wann sie das Element fokussieren soll:

Name: 

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

Der $timeout scheint nötig zu sein, um dem Modal Zeit zum Rendern zu geben.

'2.' Jedes Mal, wenn sichtbar wird (z.B. durch Klicken auf einen Button), fokussieren Sie es.

Erstellen Sie eine Direktive im Grunde wie die oben. Überwachen Sie eine Scope-Eigenschaft, und wenn sie true wird (setzen Sie sie in Ihrem ng-click handler), führen Sie element[0].focus() aus. Je nach Anwendungsfall benötigen Sie möglicherweise kein $timeout dafür:

Formular anzeigen und
 Eingabe fokussieren

   {{ myInput }}
  Formular ausblenden

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


Update 7/2013: Einige Leute haben meine Original-Direktiven mit isoliertem Scope verwendet und dann Probleme mit eingebetteten Eingabefeldern (z.B. einem Eingabefeld im Modal) gehabt. Eine Direktive ohne neuen Scope (oder möglicherweise einen neuen Kind-Scope) sollte einige Probleme lindern. Daher habe ich die Antwort oben aktualisiert, um isolierte Scopes nicht zu verwenden. Hier ist die aktualisierte Antwort:

Ursprüngliche Antwort für 1. mit isoliertem Scope:

Name: 

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.

Ursprüngliche Antwort für 2. mit isoliertem Scope:

Formular anzeigen und
 Eingabe fokussieren

  Formular ausblenden

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.

Da wir die Eigenschaft trigger/focusInput in der Direktive zurücksetzen müssen, wird '=' für die bidirektionale Datenbindung verwendet. In der ersten Direktive war '@' ausreichend. Beachten Sie auch, dass wir bei Verwendung von '@' den Trigger-Wert mit "true" vergleichen, da @ immer einen String ergibt.

269voto

Ben Lesh Punkte 106494

(EDIT: Ich habe eine aktualisierte Lösung unterhalb dieser Erklärung hinzugefügt)

Mark Rajcok ist der Mann... und seine Antwort ist eine gültige Antwort, aber sie hat hatte einen Fehler (sorry Mark)...

...Versuchen Sie, das Boolean zu verwenden, um den Fokus auf das Eingabefeld zu setzen, dann den Fokus vom Eingabefeld zu entfernen, dann versuchen Sie, es wieder zu fokussieren. Es wird nicht funktionieren, es sei denn, Sie setzen das Boolean wieder auf false zurück, dann $digest, dann setzen Sie es wieder auf true. Selbst wenn Sie einen Zeichenfolgenvergleich in Ihrem Ausdruck verwenden, werden Sie gezwungen sein, die Zeichenfolge zu ändern, $digest, dann sie wieder zurückzusetzen. (Dies wurde mit dem Blur-Ereignis-Handler behoben.)

Also schlage ich diese alternative Lösung vor:

Verwenden Sie ein Ereignis, die vergessene Funktion von Angular.

JavaScript liebt schließlich Ereignisse. Ereignisse sind von Natur aus lose gekoppelt, und noch besser, Sie vermeiden es, einen weiteren $watch zu Ihrem $digest hinzuzufügen.

app.directive('focusOn', function() {
   return function(scope, elem, attr) {
      scope.$on(attr.focusOn, function(e) {
          elem[0].focus();
      });
   };
});

Jetzt könnten Sie es so verwenden:

und dann überall in Ihrer App...

$scope.addNewItem = function () {
    /* Hier Dinge einfügen, um ein neues Element hinzuzufügen... */

    $scope.$broadcast('newItemAdded');
};

Dies ist großartig, weil Sie mit so etwas alles Mögliche machen können. Zum einen könnten Sie sich in Ereignisse einbinden, die bereits existieren. Zum anderen fangen Sie an, etwas Kluges zu tun, indem Sie verschiedene Teile Ihrer App Ereignisse veröffentlichen lassen, denen andere Teile Ihrer App folgen können.

In jeder Hinsicht schreit diese Art von Sache "ereignisgesteuert" für mich. Ich denke, als Angular-Entwickler geben wir uns wirklich Mühe, $scope-förmige Zapfen in Ereignis-förmige Löcher zu hämmern.

Ist es die beste Lösung? Ich weiß es nicht. Es ist eine Lösung.


Aktualisierte Lösung

Nach dem Kommentar von @ShimonRachlenko unten habe ich meine Methode, dies zu tun, etwas geändert. Jetzt verwende ich eine Kombination aus einem Service und einer Direktive, die ein Ereignis "hinter den Kulissen" behandelt:

Ansonsten ist es das gleiche Prinzip, wie oben beschrieben.

Hier ist ein schnelles Demo-Plunk

Verwendung

app.controller('MyCtrl', function($scope, focus) {
    focus('focusMe');
});

Quellcode

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);
    });
  }
});

244voto

ecancil Punkte 2461

Ich habe festgestellt, dass einige der anderen Antworten übermäßig kompliziert sind, wenn alles, was Sie wirklich brauchen, dies ist

app.directive('autoFocus', function($timeout) {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            $timeout(function(){
                _element[0].focus();
            }, 0);
        }
    };
});

Verwendung ist

Wir verwenden das Timeout, um die Dinge im DOM rendern zu lassen, auch wenn es null ist, wartet es zumindest darauf - so funktioniert es auch in Modalen und dergleichen

94voto

Rayron Victor Punkte 2328

63voto

JordanC Punkte 4249

Sie können auch die in Angular eingebaute jqlite-Funktionalität verwenden.

angular.element('.selector').trigger('focus');

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X