Wie kann man den Fokus auf das Eingabefeld legen?


759

Wie kann der Fokus auf das Eingabefeld in AngularJS gelegt werden?

Spezifischere Anforderungen:

  1. Wenn ein Modal geöffnet wird, konzentrieren Sie sich auf ein <input>in diesem Modal vordefiniertes .
  2. Stellen <input>Sie den Fokus jedes Mal ein, wenn er sichtbar wird (z. B. durch Klicken auf eine Schaltfläche).

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

Jede Hilfe wird geschätzt.

Antworten:


579
  1. Wenn ein Modal geöffnet wird, konzentrieren Sie sich auf einen vordefinierten <Eingang> in diesem Modal.

Definieren Sie eine Direktive und lassen Sie sie $ eine Eigenschaft / einen Trigger beobachten, damit sie weiß, wann das Element fokussiert werden muss:

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

Das $ timeout scheint erforderlich zu sein, um dem Modal Zeit zum Rendern zu geben.

'2.' Stellen Sie den Fokus jedes Mal ein, wenn <Eingabe> sichtbar wird (z. B. durch Klicken auf eine Schaltfläche).

Erstellen Sie eine Direktive im Wesentlichen wie oben. Beobachten Sie eine Scope-Eigenschaft und führen Sie sie aus, wenn sie wahr wird (legen Sie sie in Ihrem ng-click-Handler fest) element[0].focus(). Abhängig von Ihrem Anwendungsfall benötigen Sie möglicherweise ein $ Timeout für diesen:

<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


Update 7/2013 : Ich habe gesehen, dass einige Leute meine ursprünglichen Anweisungen zum Isolieren des Gültigkeitsbereichs verwenden und dann Probleme mit eingebetteten Eingabefeldern haben (dh einem Eingabefeld im Modal). Eine Richtlinie ohne neuen Geltungsbereich (oder möglicherweise mit einem neuen Geltungsbereich für Kinder) sollte einen Teil der Schmerzen lindern. Daher habe ich oben die Antwort aktualisiert, um keine isolierten Bereiche zu verwenden. Unten ist die ursprüngliche Antwort:

Ursprüngliche Antwort für 1. unter Verwendung eines isolierten Bereichs:

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 .

Ursprüngliche Antwort für 2. unter Verwendung eines isolierten Bereichs:

<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 .

Da wir die Eigenschaft trigger / focusInput in der Direktive zurücksetzen müssen, wird '=' für die bidirektionale Datenbindung verwendet. In der ersten Anweisung war '@' ausreichend. Beachten Sie auch, dass wir bei Verwendung von '@' den Triggerwert mit "true" vergleichen, da @ immer zu einer Zeichenfolge führt.


1
Siehe auch @ Joshs "focus" -Richtlinie: stackoverflow.com/a/14859639/215945 Er hat in seiner Implementierung keinen isolierten Bereich verwendet.
Mark Rajcok

2
@MarkRajcok ist nur neugierig: Diese Version funktioniert, aber wenn ich ng-modelim Eingabefeld einen Wert einstelle , geht der Modellwert verloren, wenn ich diese Direktive verwende, wobei der Isolationsbereich verwendet wird. Das Problem tritt nicht auf, wenn ich Joshs Version ohne isolierten Bereich ausprobiere. Immer noch ein Neuling, und ich würde gerne den Unterschied verstehen. Hier ist ein Plunker , der es zeigt.
Sunil D.

1
Ich fand, dass # 1 sehr gut mit AngularJS 1.0.6 funktioniert. Als ich jedoch unter einem Debugger lief, bemerkte ich, dass ich jedes Mal, wenn ich mein Modal entließ und wieder öffnete, einen zusätzlichen Aufruf der Funktion sah, die den Fokus setzt als in der Zeit zuvor. Ich habe diese Funktion leicht modifiziert, um die Uhr wann zu lösen value != "true", und das schien mein Problem zu beheben .
Andrew Brown

1
Also ... auf einer Stateful-Seite hatte ich das Gleiche vor, weil $ watch $ watch ist und eckige Entwickler $ watch lieben ... nun, ich bin auf ein Problem gestoßen, Mr @MarkRajcok, ein Problem, das ich mit der (grundlegenden) Lösung I gelöst habe unten vorgeschlagen.
Ben Lesh

3
hmm für mich funktioniert der Code, da ich sehen kann, dass er richtig ausgeführt wird und Element [0] das richtige Steuerelement ist. .focus () löst jedoch keinen Fokus aus. Möglicherweise stört Bootstrap oder etwas anderes?
Sonic Soul

264

(BEARBEITEN: Ich habe unter dieser Erklärung eine aktualisierte Lösung hinzugefügt.)

Mark Rajcok ist der Mann ... und seine Antwort ist eine gültige Antwort, aber es hat einen Defekt gehabt (sorry Mark) ...

... Versuchen Sie, den Booleschen Wert zu verwenden, um auf die Eingabe zu fokussieren, verwischen Sie dann die Eingabe und versuchen Sie dann, ihn erneut zu fokussieren. Es wird nur funktionieren, wenn Sie den Booleschen Wert auf false zurücksetzen, dann $ digest und dann wieder auf true zurücksetzen. Selbst wenn Sie in Ihrem Ausdruck einen Zeichenfolgenvergleich verwenden, müssen Sie die Zeichenfolge in etwas anderes ändern, $ Digest, und sie dann wieder ändern. (Dies wurde mit dem Unschärfe-Ereignishandler behoben.)

Also schlage ich diese alternative Lösung vor:

Verwenden Sie ein Ereignis, die vergessene Funktion von Angular.

JavaScript liebt Ereignisse schließlich. Ereignisse sind von Natur aus lose miteinander verbunden, und noch besser, Sie vermeiden es, Ihrem $ Digest eine weitere $ watch hinzuzufügen.

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

Jetzt können Sie es so verwenden:

<input type="text" focus-on="newItemAdded" />

und dann überall in Ihrer App ...

$scope.addNewItem = function () {
    /* stuff here to add a new item... */

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

Das ist großartig, weil man mit so etwas alles Mögliche machen kann. Zum einen könnten Sie an bereits vorhandene Ereignisse anknüpfen. Zum anderen machen Sie etwas Kluges, indem Sie verschiedene Teile Ihrer App Ereignisse veröffentlichen lassen, die andere Teile Ihrer App abonnieren können.

Wie auch immer, diese Art von Dingen schreit "ereignisgesteuert" zu mir. Ich denke, als Angular-Entwickler bemühen wir uns sehr, $ scope-förmige Stifte in ereignisförmige Löcher zu hämmern.

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


Aktualisierte Lösung

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

Davon abgesehen ist es das gleiche Prinzip wie oben beschrieben.

Hier ist eine kurze Demo Plunk

Verwendungszweck

<input type="text" focus-on="focusMe"/>
app.controller('MyCtrl', function($scope, focus) {
    focus('focusMe');
});

Quelle

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

3
Sie müssen den Anruf $broadcastmit abschließen, $timeoutwenn dies beim Aufrufen des Controllers funktionieren soll. Ansonsten schöne Lösung.
Shimon Rachlenko

@ ShimonRachlenko - Danke. Aber ich bin mir nicht sicher, was du mit dem $ timeout meinst. Wenn ich senden wollte, während der Controller-Konstruktor verarbeitet wurde, würde ich sofort senden. Ein Timeout würde nichts anderes tun, als die Übertragung auf eine spätere Ausführung in der Ereignisschleife zu verschieben.
Ben Lesh

1
Ja, und das reicht aus, damit die Direktive initialisiert werden kann. Übermäßig wird das Ereignis gesendet, bevor die Direktive es abhört. Dies ist wiederum nur erforderlich, wenn Sie Ihre Direktive beim Aufrufen der Seite auslösen möchten.
Shimon Rachlenko

2
Du hast Recht. Ich glaube, ich hatte es nicht benutzt, um mich auf die Last zu konzentrieren. Ich werde die Antwort mit etwas Robusterem aktualisieren.
Ben Lesh

1
Dies ist bei weitem die eleganteste "eckige" Lösung. Obwohl ich den Code meistens nur kopiert habe, als ich zum ersten Mal auf dieses Problem gestoßen bin, bin ich froh, dass Sie ein Modul dafür erstellt haben! Ich denke ehrlich, es könnte sich lohnen, zu versuchen, es in den Kernwinkel zu bringen.
Randolpho

241

Ich habe festgestellt, dass einige der anderen Antworten zu 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);
        }
    };
});

Nutzung ist

<input name="theInput" auto-focus>

Wir verwenden das Timeout, um Dinge im Dom rendern zu lassen, obwohl es Null ist, wartet es zumindest darauf - auf diese Weise funktioniert dies in Modalen und so weiter


1
Es gibt verschiedene Möglichkeiten, dies zu tun. Eine Möglichkeit, die wirklich einfach und unkompliziert ist, besteht darin, im Bereich (Controller hier) die ID des Elements festzulegen, auf das Sie sich konzentrieren möchten, wenn Sie auf die Schaltfläche klicken, und dann in Richtlinie einfach darauf hören. In diesem Fall müssten Sie die Direktive nicht an einer bestimmten Stelle platzieren, sondern nur irgendwo auf dieser Seite (ich habe diese Art von Watcher-Direktiven in der Vergangenheit mit Erfolg verwendet, insbesondere beim Fokussieren und Scrollen von Dingen) - Wenn Sie dann Wenn Sie jquery verwenden (würde dies einfacher machen), finden Sie einfach diese Element-ID und fokussieren Sie sie
ecancil

14
Eine Lösung ohne Zeitüberschreitung funktioniert bei mir nicht, wenn sich die Eingabe im Popup-Modal befindet. Aber auch 10 ms beheben das Problem
Eugene Fidelin

@ecancil: Ich mag Ihren Ansatz, weil er am einfachsten ist, aber Sie müssen das Zeitlimit im IE auf ~ 500 ms einstellen, da die Animation des modalen Erscheinungsbilds zu einem blinkenden Cursor außerhalb der Eingabe führt. Ich kenne keinen guten Weg, um dies zu erreichen, wenn die Animation endet, also erzwinge ich es brutal mit den 500ms.
Dev93

funktioniert nicht, wenn Sie mehr Daten aus der Datenbank
abrufen

1
Für diejenigen wie @Ade, die dies mit einem einfachen verwenden möchten ng-click: Nehmen wir an, das Klicken auf eine Schaltfläche hat ng-click="showInput = !showInputIhre Eingabe. Fügen Sie dann bei Ihrer tatsächlichen Eingabe hinzu ng-if="showInput". Durch Umschalten der Schaltfläche wird die Anweisung jedes Mal erneut ausgeführt. Ich hatte ein Problem damit, als ich es benutzte, ng-showwas der falsche Ansatz ist.
JVG

91

HTML hat ein Attribut autofocus.

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

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


29
Leider funktioniert dies nur einmal, wenn überhaupt. Ich habe dies für eine Angular- Bearbeitungssituation verwendet , und es hat beim ersten Mal in Chrome hervorragend funktioniert, überhaupt nicht in Firefox. Wenn ich in Chrome auf ein anderes Element klicke, um es zu bearbeiten, oder sogar wieder auf dasselbe Element, wird es nicht mehr fokussiert. Die Dokumente geben an, dass es beim Laden der Seite funktioniert, was in einer Angular-App nur einmal vorkommt. Wenn es nur so einfach wäre, würden wir alle an einem Strand sitzen und 20% verdienen!
Nocturno

62

Sie können auch die in eckig integrierte jqlite-Funktionalität verwenden.

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


2
Ohne geladene Abfrage: angle.forEach (document.querySelectorAll ('. Selector'), function (elem) {elem.focus ();});
Dan Benamy

29
Ist es nicht eine schlechte Praxis, das in einen Controller zu stecken?
VitalyB

2
Wenn es eine schlechte Praxis ist, diese JQLITE-Zeile in einen Controller einzufügen, wäre es nicht besser, diese JQLITE-Zeile in eine Direktive einzufügen?
Sport

3
Ich bekomme das:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari

1
@VitalyB Ich hatte einen Fall, in dem ich ein Element nach einem Ajax-Aufruf verwischen musste. Ich könnte nur eine Zeile reines JS in den Rückruf innerhalb des Controllers einfügen oder eine ganze Direktive ausschließlich für die Unschärfe dieser Eingabe erstellen. Ich hatte einfach das Gefühl, dass es zu mühsam war, also entschied ich mich für die erste Option.
Sebastianb

55

Dies funktioniert gut und bietet eine eckige Möglichkeit, die Eingabesteuerung zu fokussieren

angular.element('#elementId').focus()

Dies ist zwar keine reine Winkelmethode für die Ausführung der Aufgabe, die Syntax folgt jedoch dem Winkelstil. Jquery spielt indirekt eine Rolle und greift über Angular (jQLite => JQuery Light) direkt auf DOM zu.

Bei Bedarf kann dieser Code einfach in eine einfache Winkelanweisung eingefügt werden, auf die direkt zugegriffen werden kann.


Ich bin mir nicht sicher, ob dies ein großartiger Ansatz für ViewModel-Apps ist. In Angular muss dies über Anweisungen erfolgen.
Agat

zBWenn jemand eine Texbox A ändert und Sie Popup anzeigen und sich auf andere Textbox B konzentrieren müssen
Sanjeev Singh

1
Ich erhalte diesen Fehler, wenn ich Folgendes verwende:Looking up elements via selectors is not supported by jqLite!
JVG

6
Soweit ich das beurteilen kann, müssen Sie zuerst jQuery als Abhängigkeit laden. In diesem Fall angular.elementwird es zu einem Wrapper für $() / jQuery(). Ohne das wird das nicht funktionieren und Sie verwenden im Grunde sowieso nur jQuery (aber korrigieren Sie mich, wenn ich falsch
liege

@Jascination: jqLite wurde entwickelt, um die jQuery-Abhängigkeit zu entfernen. Sie benötigen nicht die gesamte jQuery, um auf ein Element in DOM zuzugreifen. Wenn Sie jedoch jQuery installiert haben, verweist Angular auf jQuery. Überprüfen Sie dies: docs.angularjs.org/api/angular.element
Sanjeev Singh

31

Ich denke nicht, dass $ timeout eine gute Möglichkeit ist, das Element auf die Erstellung zu konzentrieren. Hier ist eine Methode mit integrierter Winkelfunktionalität, die aus den trüben Tiefen der Winkeldokumente ausgegraben wurde. Beachten Sie, wie das Attribut "Link" für Pre-Link- und Post-Link-Funktionen in "Pre" und "Post" unterteilt werden kann.

Arbeitsbeispiel: 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 />

Vollständige Dokumente der AngularJS-Richtlinie: https://docs.angularjs.org/api/ng/service/$compile


2
Musste das Element [0] .focus () in ein $ timeout einschließen, damit es für mich funktioniert.
Codin

@bbodenmiller Bei meinem Modal wird ein Ausblendvorgang angewendet. Wenn ein Element erstellt wird, ist es unsichtbar (100% transparent), sodass der Browser den Fokusaufruf stillschweigend blockiert. Anscheinend erlauben einige Millisekunden, sich auf fast transparente, aber sichtbare Eingaben / Schaltflächen zu konzentrieren.
Snowindy

1
Gemäß den Dokumenten sind "Link" -Funktionen standardmäßig "postLink". siehe auch: bennadel.com/blog/…
jody tate

17

Hier ist meine ursprüngliche Lösung:

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

Und der HTML:

<button ng-click="isVisible = !isVisible">Toggle input</button>
<input ng-show="isVisible" auto-focus="{{ isVisible }}" value="auto-focus on" />

Was es macht:

Es fokussiert die Eingabe, wenn sie mit ng-show sichtbar wird. Keine Verwendung von $ watch oder $ on hier.


Eigentlich glaube ich, dass {{isVisible}} sowieso eine Uhr erstellt, daher ist die Anweisung "Keine Verwendung von $ watch" falsch.
Masimplo

Ja, ich denke du hast recht. Aber es ist immer noch eine einfache Möglichkeit, dies zu tun.
Edhowler

17

Ich habe eine Zwei-Wege-Bindungsfokus-Direktive geschrieben, genau wie das Modell kürzlich.

Sie können die Fokusanweisung wie folgt verwenden:

<input focus="someFocusVariable">

Wenn Sie eine Bereichsvariable von FocusVariable festlegen true an einer beliebigen Stelle in Ihrem Controller , wird die Eingabe fokussiert. Und wenn Sie Ihre Eingabe "verwischen" möchten, kann someFocusVariable auf false gesetzt werden. Es ist wie Mark Rajcoks erste Antwort, aber mit Zwei-Wege-Bindung.

Hier ist die Richtlinie:

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

Verwendungszweck:

<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>

Hier ist die Geige:

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


Dies sollte die richtige Antwort sein. Es deckt alle Anwendungsfälle ab.
Kunal

11

Für diejenigen, die Angular mit dem Bootstrap-Plugin verwenden:

http://angular-ui.github.io/bootstrap/#/modal

Sie können sich an das openedVersprechen der modalen Instanz halten:

modalInstance.opened.then(function() {
        $timeout(function() {
            angular.element('#title_input').trigger('focus');
        });
    });

modalInstance.result.then(function ( etc...

Gut! Allerdings ist in meinem Fall $timeoutmit 50msin Not statt 0.
lk_vc

8

Ich fand es nützlich, einen allgemeinen Ausdruck zu verwenden. Auf diese Weise können Sie beispielsweise den Fokus automatisch verschieben, wenn der eingegebene Text gültig ist

<button type="button" moo-focus-expression="form.phone.$valid">

Oder fokussieren Sie automatisch, wenn der Benutzer ein Feld mit fester Länge ausfüllt

<button type="submit" moo-focus-expression="smsconfirm.length == 6">

Und natürlich nach dem Laden fokussieren

<input type="text" moo-focus-expression="true">

Der Code für die Richtlinie:

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

7

Einen Zombie nicht wiederzubeleben oder meine eigene Anweisung zu stecken (ok, genau das mache ich):

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

Ich verwende jetzt diese Richtlinie und sie funktioniert hervorragend, löst das allgemeine Problem und ist nicht allzu komplex. Ich habe mich auf die Suche nach einer Lösung ohne Timeout gemacht. Finden Sie immer wieder Kommentare, die besagen, dass der Fokus "auf der Roadmap" für Angular 1.1 und 1.2 liegt, aber ich verwende 2.x und immer noch kein Fokusmanagement. Heh.
TekHedd

6

Ein offizieller Weg, sich zu konzentrieren, ist zunächst die Roadmap für 1.1 . In der Zwischenzeit können Sie eine Anweisung schreiben, um den Einstellungsfokus zu implementieren.

Zweitens ist eine Problemumgehung erforderlich, um den Fokus auf ein Element zu legen, nachdem es sichtbar geworden ist. Verzögern Sie einfach Ihren Aufruf von element focus () mit a $timeout.

Da das gleiche Controller-Modify-DOM-Problem für Fokus, Unschärfe und Auswahl besteht, schlage ich eine ng-targetDirektive vor:

<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>

Angular Thread hier: http://goo.gl/ipsx4 , und weitere Details hier gebloggt: http://goo.gl/4rdZa

Die folgende Anweisung erstellt eine .focus()Funktion in Ihrem Controller, die durch Ihr ng-targetAttribut angegeben wird. (Es erstellt ein .blur()und ein .select()auch.) Demo: http://jsfiddle.net/bseib/WUcQX/


+1 für die Roadmap-Referenz. Tatsächlich habe ich es gerade in der Dokumentation von 1.2 gesehen, die immer noch als instabil gilt ( docs.angularjs.org/api/ng.directive:ngFocus )
Edwin Dalorzo

27
@EdwinDalorzo ngFocusscheint eine Möglichkeit zu sein, focusEreignisse zu behandeln , nicht eine Möglichkeit, den Fokus auf ein Element zu legen .
Teleclimber

6

Anstatt eine eigene Direktive zu erstellen, können Sie einfach Javascript-Funktionen verwenden, um einen Fokus zu erzielen.

Hier ist ein Beispiel.

In der HTML-Datei:

<input type="text" id="myInputId" />

In einer Datei Javascript, beispielsweise in einem Controller, in dem Sie den Fokus aktivieren möchten:

document.getElementById("myInputId").focus();

9
Dies ist kein eckiger Weg und rät den Leuten nicht, DOM in ihren Controllern zu berühren.
Smajl

Verwenden Sie Vanille am besten nicht in Angular, da Angular sie weder verfolgen noch mit allem anderen synchron halten kann.
Edgar Quintero

4

Wenn Sie nur einen einfachen Fokus wünschen, der durch einen Klick gesteuert wird.

Html:

<input ut-focus="focusTigger">

<button ng-click="focusTrigger=!focusTrigger" ng-init="focusTrigger=false"></button>

Richtlinie:

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

4

Eine einfache, die gut mit Modalitäten funktioniert:

.directive('focusMeNow', ['$timeout', function ($timeout)
{
    return {
        restrict: 'A',

        link: function (scope, element, attrs)
        {


            $timeout(function ()
            {
                element[0].focus();
            });



        }
    };
}])

Beispiel

<input ng-model="your.value" focus-me-now />

Perfekt für mich. Vielen Dank
Alexander

3

Sie können einfach eine Direktive erstellen, die den Fokus auf das dekorierte Element in postLinking erzwingt:

angular.module('directives')
.directive('autoFocus', function() {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            _element[0].focus();
        }
    };
});

Dann in Ihrem HTML:

<input type="text" name="first" auto-focus/> <!-- this will get the focus -->
<input type="text" name="second"/>

Dies würde für Modals und ng-if-umgeschaltete Elemente funktionieren, nicht für ng-show, da postLinking nur bei der HTML-Verarbeitung erfolgt.


3

Mark und Blesh haben großartige Antworten. Marks hat jedoch einen Fehler, auf den Blesh hinweist (abgesehen davon, dass er komplex zu implementieren ist), und ich bin der Meinung, dass Bleshs Antwort einen semantischen Fehler bei der Erstellung eines Dienstes aufweist, bei dem es speziell darum geht, eine Fokusanforderung an das Frontend zu senden, wenn wirklich nur ein Weg erforderlich war verzögern Sie das Ereignis, bis alle Anweisungen abgehört haben.

Hier ist also, was ich letztendlich getan habe, was viel von Bleshs Antwort stiehlt, aber die Semantik des Controller-Ereignisses und des "After-Load" -Dienstes getrennt hält.

Dies ermöglicht es dem Controller-Ereignis, einfach für andere Dinge als nur das Fokussieren eines bestimmten Elements eingebunden zu werden, und ermöglicht es auch, den Overhead der "Nachlade" -Funktionalität nur dann zu verursachen, wenn es benötigt wird, was in vielen Fällen möglicherweise nicht der Fall ist.

Verwendungszweck

<input type="text" focus-on="controllerEvent"/>
app.controller('MyCtrl', function($scope, afterLoad) {
  function notifyControllerEvent() {
      $scope.$broadcast('controllerEvent');
  }

  afterLoad(notifyControllerEvent);
});

Quelle

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

Die einzige Antwort, die ich (ein absoluter Anfänger) verstehen konnte. Anstelle von "afterLoad (notifyControllerEvent);" habe ich "notifyControllerEvent ()" verwendet. Ansonsten endete es mit einigen Fehlern.
Vel Murugan S

3

Dies ist auch möglich zu verwenden ngModelController. Arbeiten mit 1.6+ (weiß nicht mit älteren Versionen).

HTML

<form name="myForm">
    <input type="text" name="myText" ng-model="myText">
</form>

JS

$scope.myForm.myText.$$element.focus();

- -

NB: Abhängig vom Kontext müssen Sie möglicherweise eine Timeout-Funktion einbinden.

NB²: Bei der Verwendung controllerAsist dies fast das gleiche. Ersetzen Sie einfach name="myForm"mit name="vm.myForm"und in JS, vm.myForm.myText.$$element.focus();.


3

Wahrscheinlich die einfachste Lösung für das ES6-Alter.

Durch Hinzufügen der folgenden Liner-Direktive wird das HTML-Attribut "Autofokus" für Angular.js wirksam.

.directive('autofocus', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())}))

Jetzt können Sie einfach die HTML5-Autofokus-Syntax wie folgt verwenden:

<input type="text" autofocus>

@Stephane ja, so wird es dann.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz

2

Nur ein Neuling hier, aber ich war in der Lage, es in einem ui.bootstrap.modal mit dieser Direktive zum Laufen zu bringen :

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

und in der $ modal.open-Methode habe ich das Folgende verwendet, um das Element anzugeben, auf das der Fokus gesetzt werden soll:

var d = $modal.open({
        controller : function($scope, $modalInstance) {
            ...
            $scope.idToFocus = "cancelaAteste";
    }
        ...
    });

Auf der Vorlage habe ich folgendes:

<input id="myInputId" focus />

2

Die folgende Anweisung hat den Trick für mich getan. Verwenden Sie für die Eingabe dasselbe Autofokus-HTML-Attribut.

.directive('autofocus', [function () {
    return {
        require : 'ngModel',
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.focus();
        }
    };
}])

1
Sie erhalten eine Fehlermeldung, wenn jQuery nicht enthalten ist. Es sollte stattdessen Element [0] sein .focus ()
Ryan M

2

Wenn Sie modalInstance verwenden und über das Objekt verfügen, können Sie "then" verwenden, um nach dem Öffnen des Modals Aktionen auszuführen. Wenn Sie modalInstance nicht verwenden und fest codiert sind, um das Modal zu öffnen, können Sie das Ereignis verwenden. Das $ timeout ist keine gute Lösung.

Sie können Folgendes tun (Bootstrap3):

$("#" + modalId).on("shown.bs.modal", function() {
    angular.element("[name='name']").focus();
});

Bei modalInstance können Sie in der Bibliothek nachsehen, wie der Code nach dem Öffnen von modal ausgeführt wird.

Verwenden Sie $ timeout nicht so, das $ timeout kann 0, 1, 10, 30, 50, 200 oder mehr sein. Dies hängt vom Client-Computer und dem Vorgang zum Öffnen von Modal ab.

Verwenden Sie nicht $ timeout. Lassen Sie sich von der Methode mitteilen, wann Sie sich konzentrieren können.

Ich hoffe das hilft! :) :)


2

Die gesamte vorherige Antwort funktioniert nicht, wenn das gewünschte Fokuselement in eine Direktivenvorlage eingefügt wird. Die folgende Direktive passt sowohl zu einfachen Elementen als auch zu injizierten Direktiven (ich habe sie in Typoskript geschrieben ). es akzeptiert Selektor für inneres fokussierbares Element. Wenn Sie nur das Selbstelement fokussieren müssen, senden Sie keinen Selektorparameter an die Direktive:

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

}}

Anwendungsbeispiel für einfaches Element:

<button tabindex="0" focus-on-load />

Anwendungsbeispiel für inneres Element (normalerweise für dynamisch injiziertes Element wie Direktive mit Vorlage):

<my-directive focus-on-load="input" />

Sie können einen beliebigen jQuery-Selektor anstelle von "Eingabe" verwenden.


2

Ich bearbeite die focusMe-Direktive von Mark Rajcok, um für mehrere Fokusse in einem Element zu arbeiten.

HTML:

<input  focus-me="myInputFocus"  type="text">

in AngularJs Controller:

$scope.myInputFocus= true;

AngulaJS-Richtlinie:

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

1

Ich möchte zu dieser Diskussion beitragen, nachdem ich nach einer besseren Lösung gesucht und sie nicht gefunden habe, sondern sie stattdessen erstellen muss.

Kriterien: 1. Die Lösung sollte unabhängig vom Umfang des übergeordneten Controllers sein, um die Wiederverwendbarkeit zu verbessern. 2. Vermeiden Sie die Verwendung von $ watch, um einen bestimmten Zustand zu überwachen. Dies ist langsam, vergrößert die Digest-Schleife und erschwert das Testen. 3. Vermeiden Sie $ timeout oder $ scope. $ Apply (), um eine Digest-Schleife auszulösen. 4. In dem Element, in dem die Richtlinie verwendet wird, befindet sich ein Eingabeelement.

Dies ist die Lösung, die mir am besten gefallen hat:

Richtlinie:

.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>

Ich hoffe das hilft jemandem da draußen!


Sie haben gerade neu erstellt, was HTML "label" macht. Sie könnten einfach "div focus-input" durch "label" ersetzen und die Direktive entfernen.
frst

1

Es ist einfach ... versuchen Sie es

html

<select id="ddl00">  
 <option>"test 01"</option>  
</select>

Javascript

document.getElementById("ddl00").focus();

Das ist aus dem Zusammenhang heraus korrekt, aber nicht die AngularJS-Methode
CodeMonkey

1

Wenn Sie den Fokus auf ein bestimmtes Element legen möchten, können Sie den folgenden Ansatz verwenden.

  1. Erstellen Sie einen Dienst namens focus.

    angular.module('application')
    .factory('focus', function ($timeout, $window) {
        return function (id) {
            $timeout(function () {
                var element = $window.document.getElementById(id);
                if (element)
                    element.focus();
            });
        };
    });
  2. Injizieren Sie es in den Controller, von dem aus Sie anrufen möchten.

  3. Rufen Sie diesen Service an.


0

Ich halte die Richtlinie für unnötig. Verwenden Sie HTML-ID- und Klassenattribute, um das erforderliche Element auszuwählen, und lassen Sie den Dienst document.getElementById oder document.querySelector verwenden, um den Fokus (oder jQuery-Äquivalente) anzuwenden.

Markup ist eine Standard-HTML- / Winkelanweisung mit hinzugefügten IDs / Klassen zur Auswahl

<input id="myInput" type="text" ng-model="myInputModel" />

Controller sendet Ereignis

$scope.$emit('ui:focus', '#myInput');

Im UI-Dienst wird querySelector verwendet. Wenn mehrere Übereinstimmungen vorliegen (z. B. aufgrund der Klasse), wird nur die erste zurückgegeben

$rootScope.$on('ui:focus', function($event, selector){
  var elem = document.querySelector(selector);
  if (elem) {
    elem.focus();
  }
});

Möglicherweise möchten Sie $ timeout () verwenden, um einen Digest-Zyklus zu erzwingen


0

Wirf einfach einen Kaffee hinein.

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.