วิธีตั้งค่าโฟกัสบนฟิลด์อินพุต


759

'วิธีเชิงมุม' เพื่อกำหนดโฟกัสไปที่ฟิลด์อินพุตใน AngularJS คืออะไร

ข้อกำหนดเฉพาะเพิ่มเติม:

  1. เมื่อModalเปิดขึ้นให้ตั้งค่าโฟกัสที่กำหนดไว้ล่วงหน้า<input>ภายใน Modal นี้
  2. ทุกครั้งที่<input>มองเห็นได้ (เช่นโดยคลิกปุ่มบางปุ่ม) ตั้งโฟกัสไว้

ฉันพยายามทำตามข้อกำหนดแรกด้วยautofocusแต่วิธีนี้จะใช้ได้เฉพาะเมื่อ Modal เปิดขึ้นเป็นครั้งแรกและเฉพาะในเบราว์เซอร์บางตัวเท่านั้น (เช่นใน Firefox จะไม่ทำงาน)

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชม.

คำตอบ:


579
  1. เมื่อ Modal เปิดขึ้นให้ตั้งโฟกัสไปที่ <input> ที่กำหนดไว้ล่วงหน้าภายใน Modal นี้

กำหนดคำสั่งและให้มัน $ เฝ้าดูสถานที่ให้บริการ / ทริกเกอร์เพื่อให้มันรู้ว่าเมื่อใดที่จะมุ่งเน้นองค์ประกอบ:

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

ดูเหมือนว่าการหมดเวลาของ $ จะต้องใช้เวลาในการแสดงผล

'2' ทุกครั้งที่ <input> ปรากฏให้เห็น (เช่นโดยคลิกที่ปุ่ม) ตั้งโฟกัสไว้

สร้างคำสั่งเป็นหลักเช่นเดียวกับข้างต้น ดูคุณสมบัติขอบเขตบางส่วนและเมื่อมันกลายเป็นจริง (ตั้งค่าในการจัดการงะคลิกของคุณ) 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


Update 7/2013 : ฉันเห็นบางคนใช้คำสั่งขอบเขต isolate ดั้งเดิมของฉันแล้วมีปัญหากับฟิลด์อินพุตแบบฝัง (เช่นฟิลด์อินพุตในโมดอล) คำสั่งที่ไม่มีขอบเขตใหม่ (หรืออาจเป็นขอบเขตเด็กใหม่) ควรบรรเทาความเจ็บปวดบางส่วน ดังนั้นด้านบนฉันปรับปรุงคำตอบที่จะไม่ใช้ขอบเขตแยก ด้านล่างนี้เป็นคำตอบเดิม:

คำตอบดั้งเดิมสำหรับ 1. โดยใช้ขอบเขต isolate:

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 โดยใช้ขอบเขต isolate:

<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

เนื่องจากเราจำเป็นต้องรีเซ็ตคุณสมบัติทริกเกอร์ / focusInput ในคำสั่ง '=' จะใช้สำหรับการบันทึกข้อมูลแบบสองทาง ในคำสั่งแรก '@' ก็เพียงพอแล้ว โปรดทราบว่าเมื่อใช้ '@' เราจะเปรียบเทียบค่าทริกเกอร์กับ "true" เนื่องจาก @ จะให้ผลลัพธ์เป็นสตริงเสมอ


1
โปรดดูคำสั่ง "โฟกัส" ของ @ Josh: stackoverflow.com/a/14859639/215945 เขาไม่ได้ใช้ขอบเขตไอโซเลทในการนำไปปฏิบัติ
Mark Rajcok

2
@ MarkRajcok เพียงแค่อยากรู้เกี่ยวกับเรื่องนี้: รุ่นนี้ใช้งานได้ แต่ถ้าฉันตั้งค่าng-modelในช่องใส่ค่ารุ่นจะหายไปเมื่อฉันใช้คำสั่งนี้ w / ขอบเขต isolate จะใช้ ปัญหาไม่เกิดขึ้นถ้าฉันลองใช้เวอร์ชันของ Josh ด้วยขอบเขตแยก ยังเป็นมือใหม่และฉันต้องการเข้าใจความแตกต่าง นี่คือ Plunkerที่แสดงมัน
Sunil D.

1
ฉันพบว่า # 1 ทำงานได้ดีมากกับ AngularJS 1.0.6 อย่างไรก็ตามเมื่อทำงานภายใต้ดีบักเกอร์ฉันสังเกตเห็นว่าทุกครั้งที่ฉันออกและเปิด modal ของฉันฉันก็เห็นการเรียกเพิ่มเติมไปยังฟังก์ชันที่กำหนดโฟกัสมากกว่าครั้งก่อน ฉันปรับเปลี่ยนฟังก์ชั่นนั้นเล็กน้อยเพื่อยกเลิกการผูกสายนาฬิกาเมื่อvalue != "true"และนั่นก็เพื่อแก้ไขปัญหาของฉัน
Andrew Brown

1
ดังนั้น ... บนเพจที่มีสถานะฉันมีสิ่งเดียวกันที่เกิดขึ้นเพราะ $ watch คือ $ watch และนักพัฒนาเชิงมุมรัก $ watch ... ดีฉันประสบปัญหา Mr @ MarkRajcok ปัญหาที่ฉันแก้ไขด้วยโซลูชัน (พื้นฐาน) ฉัน เสนอด้านล่าง
เบ็นเลช

3
hmm สำหรับฉันรหัสทำงานตามที่ฉันเห็นมันทำงานอย่างถูกต้องและองค์ประกอบ [0] คือการควบคุมที่เหมาะสม อย่างไรก็ตาม. โฟกัส () ไม่ทำให้เกิดการโฟกัส อาจ bootstrap หรืออย่างอื่นรบกวนนี้
Sonic Soul

264

(แก้ไข: ฉันได้เพิ่มโซลูชันที่ปรับปรุงด้านล่างคำอธิบายนี้)

Mark Rajcok เป็นผู้ชาย ... และคำตอบของเขาคือคำตอบที่ถูกต้อง แต่มีข้อบกพร่อง (ขออภัยมาร์ก) ...

... ลองใช้บูลีนเพื่อโฟกัสไปที่อินพุตจากนั้นเบลออินพุตจากนั้นลองใช้มันเพื่อโฟกัสอินพุตอีกครั้ง มันจะไม่ทำงานจนกว่าคุณจะรีเซ็ตบูลีนให้เป็นเท็จจากนั้นทำการแยกย่อย $ จากนั้นรีเซ็ตมันกลับเป็นจริง แม้ว่าคุณจะใช้การเปรียบเทียบสตริงในนิพจน์ของคุณคุณจะถูกบังคับให้เปลี่ยนสตริงเป็นอย่างอื่น $ แยกย่อยแล้วเปลี่ยนกลับ(สิ่งนี้ได้รับการแก้ไขด้วยตัวจัดการเหตุการณ์เบลอ)

ดังนั้นฉันเสนอวิธีแก้ปัญหาอื่น:

ใช้เหตุการณ์คุณลักษณะที่ถูกลืมของ Angular

จาวาสคริปต์ชอบกิจกรรมหลังจากทั้งหมด เหตุการณ์ต่าง ๆ จะถูกรวมเข้าด้วยกันอย่างหลวม ๆ และยิ่งกว่านั้นคุณหลีกเลี่ยงการเพิ่มเงินอีก $ 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');
};

มันยอดเยี่ยมมากเพราะคุณสามารถทำสิ่งต่าง ๆ ได้ทุกอย่างด้วยสิ่งนี้ สำหรับหนึ่งคุณสามารถผูกเข้ากับกิจกรรมที่มีอยู่แล้ว อีกสิ่งหนึ่งที่คุณเริ่มทำสิ่งที่ชาญฉลาดโดยให้ส่วนต่าง ๆ ของแอพของคุณเผยแพร่กิจกรรมที่ส่วนอื่น ๆ ของแอพของคุณสามารถสมัครสมาชิกได้

ยังไงก็ตามสิ่งประเภทนี้กรีดร้อง "เหตุการณ์ที่เกิดขึ้น" กับฉัน ฉันคิดว่าในฐานะนักพัฒนาแองกูลาร์เราพยายามอย่างหนักที่จะตอกหมุดดอลล่าร์ในรูปทรงขอบเขต $ ให้เป็นรูรูปเหตุการณ์

มันเป็นทางออกที่ดีที่สุด? ไม่รู้สินะ. มันเป็นทางออก


โซลูชั่นที่อัปเดตแล้ว

หลังจากความคิดเห็นของ @ 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);
    });
  }
});

3
คุณต้องปิดการโทร$broadcastด้วย$timeoutหากคุณต้องการให้สิ่งนี้ทำงานบนการเข้าสู่คอนโทรลเลอร์ ทางออกที่ดีเป็นอย่างอื่น
Shimon Rachlenko

@ShimonRachlenko - ขอบคุณ แต่ฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดยการหมดเวลา $ ถ้าฉันต้องการออกอากาศเมื่อตัวควบคุมคอนโทรลเลอร์กำลังประมวลผลอยู่ฉันก็จะออกอากาศทันที การหมดเวลาจะไม่ทำอะไรเลยนอกจากเลื่อนการออกอากาศไปยังการดำเนินการในภายหลังในลูปของเหตุการณ์
เบ็นเลช

1
ใช่และนั่นก็เพียงพอแล้วสำหรับคำสั่งในการเริ่มต้น โอเวอร์ไซด์เหตุการณ์ที่มีการออกอากาศก่อนที่คำสั่งจะเริ่มฟัง .. อีกครั้งสิ่งนี้จำเป็นเฉพาะเมื่อคุณต้องการเรียกใช้คำสั่งของคุณเมื่อคุณเข้าสู่หน้า
Shimon Rachlenko

2
คุณถูกต้อง ฉันเดาว่าฉันไม่ได้ใช้มันเพื่อมุ่งเน้นไปที่โหลด ฉันจะอัปเดตคำตอบด้วยสิ่งที่แข็งแกร่งกว่า
เบ็นเลช

1
นี่คือทางออก "เชิงมุม" ที่หรูหราที่สุด แม้ว่าส่วนใหญ่ฉันเพิ่งคัดลอกรหัสเมื่อฉันพบปัญหานี้ครั้งแรกฉันดีใจที่คุณทำโมดูลสำหรับมัน! ฉันคิดว่ามันคงคุ้มค่าที่จะลองเข้าไปที่มุมแกนกลาง
Randolpho

241

ฉันได้พบคำตอบอื่น ๆ ที่จะซับซ้อนเกินไปเมื่อสิ่งที่คุณต้องการจริงๆคือสิ่งนี้

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

การใช้งานคือ

<input name="theInput" auto-focus>

เราใช้การหมดเวลาเพื่อให้สิ่งต่าง ๆ ใน dom เรนเดอร์แม้ว่ามันจะเป็นศูนย์ แต่อย่างน้อยก็ต้องรออย่างนั้น - วิธีนี้ใช้งานได้ใน modals และอะไรก็ตาม


1
จะมีหลายวิธีในการทำเช่นนี้วิธีหนึ่งที่เป็นไปได้ที่ตรงไปตรงมาและง่ายต่อการจะอยู่ในขอบเขต (ตัวควบคุมที่นี่) ตั้งค่า ID ของรายการที่คุณต้องการมุ่งเน้นเมื่อคุณคลิกปุ่มจากนั้นใน คำสั่งเพียงฟังนี้ ในกรณีนี้คุณไม่จำเป็นต้องวางคำสั่งไว้ที่ใดโดยเฉพาะอยู่ที่ไหนสักแห่งในหน้านั้น (ฉันเคยใช้คำสั่งเฝ้าดูแบบนี้กับความสำเร็จในอดีตโดยเฉพาะอย่างยิ่งกับการเน้นสิ่งต่าง ๆ และเลื่อนสิ่งของ) - ถ้าคุณ กำลังใช้ jquery (จะทำให้ง่ายขึ้น) เพียงแค่ค้นหารหัสองค์ประกอบนั้นและมุ่งเน้น
ecancil

14
โซลูชันที่ไม่มีการหมดเวลาเป็นศูนย์จะไม่ทำงานสำหรับฉันหากอินพุตอยู่ใน modal ของป๊อปอัป แต่แม้กระทั่ง 10 มิลลิวินาทีก็สามารถแก้ไขปัญหาได้
Eugene Fidelin

@ecancil: ฉันชอบวิธีการของคุณเพราะมันง่ายที่สุด แต่คุณต้องตั้งค่าการหมดเวลาเป็น ~ 500ms ใน IE เพราะการเคลื่อนไหวของลักษณะที่ปรากฏเป็นกิริยาช่วยให้เคอร์เซอร์กะพริบอยู่ด้านนอกของอินพุต ฉันไม่รู้วิธีที่ดีที่จะมีสิ่งนี้เกิดขึ้นเมื่ออนิเมชั่นสิ้นสุดลงดังนั้นฉันจึงดุร้ายด้วย 500 มิลลิวินาที
Dev93

จะไม่ทำงานถ้าคุณต้องดึงข้อมูลจากฐานข้อมูลมากขึ้นก็ต้องรอให้คอนโทรลเลอร์ทำการดึงข้อมูลจนเสร็จสมบูรณ์ดังนั้นเพิ่มเวลาให้เพียงพอแทน 0
Ali Adravi

1
สำหรับผู้ที่ชอบ @Ade ที่ต้องการใช้สิ่งนี้ด้วยวิธีง่าย ๆng-click: สมมติว่าการคลิกปุ่มng-click="showInput = !showInputบนอินพุตของคุณ ng-if="showInput"จากนั้นในการป้อนข้อมูลที่เกิดขึ้นจริงของคุณเพิ่มใน การกดปุ่มจะทำให้คำสั่งทำงานซ้ำในแต่ละครั้ง ฉันพบปัญหาขณะใช้งานng-showซึ่งเป็นวิธีการที่ผิด
JVG

91

HTML autofocusมีแอตทริบิวต์

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

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


29
น่าเสียดายที่นี่ใช้งานได้เพียงครั้งเดียวถ้าทั้งหมด ฉันใช้สิ่งนี้กับสถานการณ์การแก้ไขแบบ Angular และใช้งานได้ดีใน Chrome เป็นครั้งแรกไม่ใช่ใน Firefox ใน Chrome เมื่อฉันคลิกไอเท็มอื่นเพื่อแก้ไขหรือแม้แต่ในไอเท็มเดียวกันอีกครั้งมันจะไม่ได้รับการโฟกัสอีกต่อไป เอกสารระบุว่ามันทำงานกับการโหลดหน้าเว็บซึ่งจะเกิดขึ้นเพียงครั้งเดียวในแอป Angular ถ้ามันง่ายขนาดนี้พวกเราทุกคนจะนั่งบนชายหาดที่ได้รับ 20%!
Nocturno

62

คุณยังสามารถใช้ฟังก์ชั่น jqlite ที่สร้างขึ้นในเชิงมุม

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


2
ไม่โหลด jquery: angular.forEach (document.querySelectorAll ('. selector'), ฟังก์ชัน (elem) {elem.focus ();});
Dan Benamy

29
มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะนำสิ่งนั้นไปไว้ในคอนโทรลเลอร์?
VitalyB

2
หากการวางสาย jqlite นี้ไว้ในตัวควบคุมเป็นแนวปฏิบัติที่ไม่ดีมันจะดีกว่าหรือไม่ที่จะวางสาย jqlite นี้ไว้ในคำสั่ง?
กีฬา

3
ฉันได้รับสิ่งนี้:Looking up elements via selectors is not supported by jqLite!
Maria Ines Parnisari

1
@VitalyB ฉันมีกรณีที่ฉันต้องการเบลอองค์ประกอบหลังจากโทร ajax ฉันสามารถเพิ่ม JS บริสุทธิ์เพียงบรรทัดเดียวในการติดต่อกลับภายในตัวควบคุมหรือสร้างคำสั่งทั้งหมดสำหรับเบลอของอินพุตนั้นโดยเฉพาะ ฉันแค่รู้สึกว่ามันยุ่งยากมากเกินไปดังนั้นฉันจึงไปที่ตัวเลือกแรก
Sebastianb

55

วิธีนี้ทำงานได้ดีและเป็นวิธีเชิงมุมในการมุ่งเน้นการควบคุมอินพุต

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

แม้ว่านี่จะไม่ใช่วิธีเชิงมุมที่บริสุทธิ์ในการทำงาน แต่ไวยากรณ์ก็เป็นไปตามลักษณะเชิงมุม Jquery มีบทบาททางอ้อมและเข้าถึง DOM โดยตรงโดยใช้ Angular (jQLite => JQuery Light)

หากต้องการรหัสนี้สามารถใส่ไว้ในคำสั่งเชิงมุมอย่างง่าย ๆ ซึ่งองค์ประกอบสามารถเข้าถึงได้โดยตรง


ฉันไม่แน่ใจว่าเป็นวิธีการที่ยอดเยี่ยมสำหรับ ViewModel-apps ในเชิงมุมจะต้องทำผ่านคำสั่ง
Agat

เช่นหากมีคนเปลี่ยนแปลง texbox A และคุณต้องแสดงป๊อปอัพและตั้งโฟกัสที่กล่องข้อความอื่น B.
Sanjeev Singh

1
ฉันได้รับข้อผิดพลาดนี้เมื่อใช้สิ่งนี้:Looking up elements via selectors is not supported by jqLite!
JVG

6
เท่าที่ผมสามารถบอกได้ว่าคุณจะต้องโหลด jQuery เป็น dependancy แรกซึ่งในกรณีนี้จะกลายเป็นเสื้อคลุมสำหรับangular.element $() / jQuery()โดยไม่ต้องว่านี้จะไม่ทำงานและคุณพื้นเพียงใช้ jQuery อยู่แล้ว ( แต่ถูกต้องฉันหากฉันผิด)
JVG

@Jascination: jqLite พัฒนาขึ้นเพื่อลบการพึ่งพา jQuery คุณไม่ต้องการ jQuery ทั้งหมดเพื่อเข้าถึงองค์ประกอบใน DOM แต่ถ้าคุณติดตั้ง jQuery แล้ว Angular จะอ้างถึง jQuery ตรวจสอบสิ่งนี้: docs.angularjs.org/api/angular.element
Sanjeev Singh

31

ฉันไม่คิดว่าการหมดเวลา $ เป็นวิธีที่ดีในการมุ่งเน้นองค์ประกอบในการสร้าง นี่คือวิธีการใช้ฟังก์ชั่นเชิงมุมที่มีอยู่ภายในขุดออกมาจากส่วนลึกที่มืดมนของเอกสารเชิงมุม สังเกตว่าคุณลักษณะ "ลิงก์" สามารถแบ่งออกเป็น "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: https://docs.angularjs.org/api/ng/service/$compile


2
ต้องห่อองค์ประกอบ [0] .focus () ใน $ timeout เพื่อให้มันใช้งานได้สำหรับฉัน
codin

@bodenmiller modal ของฉันมีสิ่งที่หายไปเมื่อองค์ประกอบได้รับการสร้างมันจะมองไม่เห็น (โปร่งใส 100%) ดังนั้นเบราว์เซอร์บล็อกโฟกัสภาวนาอย่างเงียบ ๆ เห็นได้ชัดว่าบางมิลลิวินาทีผ่านไปแล้วอนุญาตให้มุ่งเน้นไปที่ปุ่มอินพุต / ปุ่มที่โปร่งใสเกือบมองเห็นได้
snowindy

1
ตามเอกสารฟังก์ชั่น "ลิงก์" คือ "postLink" โดยค่าเริ่มต้น ดูเพิ่มเติมที่: bennadel.com/blog/…
jody tate

17

นี่คือโซลูชันดั้งเดิมของฉัน:

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 ที่นี่


ที่จริงแล้วฉันเชื่อว่า {{isVisible}} กำลังสร้างนาฬิกาอยู่แล้วดังนั้นคำสั่ง "ไม่มีการใช้ $ watch" จึงไม่ถูกต้อง
masimplo

ใช่ฉันคิดว่าคุณพูดถูก แต่มันก็ยังทำหน้าที่เป็นวิธีที่ง่ายที่จะทำ
Edhowler

17

ฉันได้เขียนคำสั่งการโฟกัสสองทิศทางที่มีผลผูกพันเช่นเดียวกับแบบจำลองเมื่อเร็ว ๆ นี้

คุณสามารถใช้คำสั่งโฟกัสเช่นนี้:

<input focus="someFocusVariable">

หากคุณสร้างตัวแปรขอบเขต FocusVariable trueได้ทุกที่ในคอนโทรลเลอร์ของคุณอินพุตจะถูกโฟกัส และถ้าคุณต้องการที่จะ "เบลอ" อินพุตของคุณก็สามารถตั้งค่า FocusVariable เป็นเท็จได้ มันเหมือนคำตอบแรกของ Mark Rajcok แต่มีผลผูกพันสองทาง

นี่คือคำสั่ง:

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/


นี่ควรเป็นคำตอบที่ถูกต้อง ครอบคลุมทุกกรณีการใช้งาน
Kunal

11

สำหรับผู้ที่ใช้ Angular กับปลั๊กอิน Bootstrap:

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

คุณสามารถขอopenedสัญญาของอินสแตนซ์ของ modal ได้:

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

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

ดี! อย่างไรก็ตามในกรณีของฉัน$timeoutที่มีอยู่ในความต้องการแทน50ms 0
lk_vc

8

ฉันพบว่ามีประโยชน์ในการใช้นิพจน์ทั่วไป วิธีนี้คุณสามารถทำสิ่งต่าง ๆ เช่นย้ายโฟกัสโดยอัตโนมัติเมื่อป้อนข้อความถูกต้อง

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

7

ที่จะไม่ชุบชีวิตผีดิบหรือเสียบคำสั่งของฉันเอง (ตกลงนั่นคือสิ่งที่ฉันทำ):

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

ตอนนี้ฉันใช้คำสั่งนี้และมันใช้งานได้ดีแก้ปัญหาทั่วไปและไม่ซับซ้อนเกินไป หาวิธีแก้ปัญหาที่ไม่ต้องเสียค่าใช้จ่าย $ การค้นหาความคิดเห็นที่บอกว่าโฟกัสคือ "ในแผนงาน" สำหรับ Angular 1.1 และ 1.2 แต่ฉันใช้ 2.x และยังไม่มีการจัดการโฟกัส หึ
tekHedd

6

ก่อนอื่นวิธีที่เป็นทางการที่จะมุ่งเน้นคือแผนงานสำหรับ 1.11.1 ในขณะเดียวกันคุณสามารถเขียนคำสั่งเพื่อใช้การตั้งค่าโฟกัส

ประการที่สองการตั้งค่าโฟกัสที่รายการหลังจากมองเห็นได้ในขณะนี้ต้องใช้วิธีแก้ไข เพียงแค่ชะลอการเรียกร้องให้โฟกัสองค์ประกอบ () $timeoutด้วย

เนื่องจากปัญหา controller-modified-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/


+1 สำหรับการอ้างอิงแผนงาน ในความเป็นจริงฉันเพิ่งเห็นมันในเอกสารของ 1.2 ยังถือว่าไม่เสถียร ( docs.angularjs.org/api/ng.directive:ngFocus )
Edwin Dalorzo

27
@EdwinDalorzo ngFocusดูเหมือนจะเป็นวิธีการจัดการfocusเหตุการณ์ไม่ใช่วิธีการตั้งค่าการมุ่งเน้นองค์ประกอบ
เทเลคอมเบอร์

6

แทนที่จะสร้างคำสั่งของคุณเองคุณสามารถใช้ฟังก์ชั่นจาวาสคริปต์เพื่อให้ได้โฟกัส

นี่คือตัวอย่าง

ในไฟล์ html:

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

ในไฟล์ javascript ในคอนโทรลเลอร์ตัวอย่างเช่นที่คุณต้องการเปิดใช้งานโฟกัส:

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

9
นี่ไม่ใช่ 'เชิงมุม' และไม่แนะนำให้ผู้ใช้แตะ DOM ในส่วนควบคุม
smajl

ดีที่สุดที่จะไม่ใช้วานิลลาใน Angular เพราะ Angular ไม่สามารถติดตามหรือเก็บข้อมูลให้ตรงกันกับสิ่งอื่น ๆ ได้
Edgar Quintero

4

หากคุณต้องการโฟกัสที่เรียบง่ายซึ่งควบคุมโดย ng-click

Html:

<input ut-focus="focusTigger">

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

Directive:

'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

ง่าย ๆ ที่ทำงานได้ดีกับ modals:

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

เหมาะสำหรับฉัน ขอบคุณ
Alexander

3

คุณสามารถสร้างคำสั่งที่บังคับให้เน้นองค์ประกอบที่ตกแต่งในการโพสต์ลิงก์:

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-show เนื่องจาก postLinking เกิดขึ้นเฉพาะในการประมวลผล HTML


3

Mark และ Blesh มีคำตอบที่ดี อย่างไรก็ตามมาร์คมีข้อบกพร่องที่ Blesh ชี้ให้เห็น (นอกเหนือจากความซับซ้อนในการนำไปใช้) และฉันรู้สึกว่าคำตอบของ Blesh มีข้อผิดพลาดทางความหมายในการสร้างบริการที่เฉพาะเจาะจงเกี่ยวกับการส่งคำขอโฟกัสไปยังส่วนหน้าเมื่อทุกสิ่งที่เขาต้องการ ชะลอกิจกรรมจนกว่าคำสั่งทั้งหมดจะฟัง

ดังนั้นนี่คือสิ่งที่ฉันทำซึ่งขโมยมากจากคำตอบของ Blesh แต่ทำให้ความหมายของเหตุการณ์คอนโทรลเลอร์และบริการ "after load" แยกกัน

สิ่งนี้จะช่วยให้เหตุการณ์คอนโทรลเลอร์นั้นสามารถเชื่อมโยงกับสิ่งอื่น ๆ ได้อย่างง่ายดายนอกเหนือจากการมุ่งเน้นไปที่องค์ประกอบเฉพาะและยังช่วยให้สามารถใช้งานฟังก์ชั่น "after load" ได้หากต้องการซึ่งอาจไม่ได้ในหลายกรณี

การใช้

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

คำตอบเดียวเท่านั้นซึ่งฉัน (ผู้เริ่มต้นสมบูรณ์) สามารถเข้าใจได้ แทนที่จะเป็น "afterLoad (alertControllerEvent);" ฉันใช้เพื่อ "alertControllerEvent ()" มิฉะนั้นจะจบลงด้วยข้อผิดพลาดบางอย่าง
Vel Murugan S

3

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"และ vm.myForm.myText.$$element.focus();JS,


3

อาจเป็นทางออกที่ง่ายที่สุดในยุค ES6

การเพิ่มคำสั่ง Liner ต่อไปนี้ทำให้ HTML 'โฟกัสอัตโนมัติ' มีประสิทธิภาพใน Angular.js

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

ตอนนี้คุณสามารถใช้ไวยากรณ์โฟกัสอัตโนมัติ HTML5 เช่น:

<input type="text" autofocus>

@ สเตฟานใช่แล้วมันจะกลายเป็น.directive('autofocus', ['$timeout', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())})])
Michael Plautz

2

เพียงแค่สมัครใหม่ที่นี่ แต่ฉันก็สามารถที่จะทำให้มันทำงานใน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 ฉันใช้ folowing เพื่อระบุองค์ประกอบที่ควรให้ความสำคัญ:

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

บนเทมเพลตฉันมีสิ่งนี้:

<input id="myInputId" focus />

2

คำสั่งต่อไปนี้ได้หลอกลวงสำหรับฉัน ใช้แอตทริบิวต์ html โฟกัสอัตโนมัติเดียวกันสำหรับอินพุต

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

1
คุณจะได้รับข้อผิดพลาดในการทำงานนี้เว้นแต่จะรวม jQuery ควรเป็นองค์ประกอบ [0] .focus ()
Ryan M

2

หากคุณใช้ modalInstance และมีวัตถุคุณสามารถใช้ "แล้ว" เพื่อดำเนินการหลังจากเปิด modal หากคุณไม่ได้ใช้ modalInstance และฮาร์ดโค้ดเพื่อเปิด modal คุณสามารถใช้เหตุการณ์ได้ การหมดเวลาของ $ ไม่ใช่วิธีแก้ปัญหาที่ดี

คุณสามารถทำได้ (Bootstrap3):

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

ที่ modalInstance คุณสามารถดูไลบรารี่ได้ว่าจะรันโค้ดอย่างไรหลังจากเปิดโมดัล

อย่าใช้การหมดเวลา $ เช่นนี้การหมดเวลา $ อาจเป็น 0, 1, 10, 30, 50, 200 หรือมากกว่านี้ขึ้นอยู่กับคอมพิวเตอร์ไคลเอนต์และกระบวนการในการเปิดเป็นกิริยาช่วย

อย่าใช้การหมดเวลา $ ให้วิธีบอกคุณเมื่อคุณสามารถมุ่งเน้น;)

ฉันหวังว่าจะช่วยได้! :)


2

คำตอบก่อนหน้านี้ทั้งหมดไม่ทำงานหากองค์ประกอบโฟกัสที่ต้องการถูกแทรกลงในแม่แบบคำสั่ง คำสั่งดังต่อไปนี้พอดีกับองค์ประกอบที่เรียบง่ายหรือองค์ประกอบฉีด Directive (ฉันเขียนไว้ในtypescript ) ยอมรับตัวเลือกสำหรับองค์ประกอบที่สามารถโฟกัสได้ภายใน หากคุณต้องการโฟกัสองค์ประกอบของตัวเอง - อย่าส่งพารามิเตอร์ตัวเลือกใด ๆ ไปยังคำสั่ง:

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 แทน "อินพุต"


2

ฉันแก้ไขคำสั่ง 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);
                }
            });
        }
    };
});

1

ฉันต้องการมีส่วนร่วมในการสนทนานี้หลังจากค้นหาวิธีการแก้ปัญหาที่ดีกว่าและไม่พบมันต้องสร้างมันแทน

เกณฑ์: 1. โซลูชันควรเป็นอิสระจากขอบเขตของตัวควบคุมพาเรนต์เพื่อเพิ่มการใช้งานซ้ำได้ 2. หลีกเลี่ยงการใช้ $ watch เพื่อตรวจสอบเงื่อนไขบางอย่างซึ่งช้าทั้งเพิ่มขนาดของลูปย่อยและทำให้การทดสอบยากขึ้น 3. หลีกเลี่ยง $ timeout หรือ $ scope $ Apply () เพื่อทริกเกอร์วนย่อย 4. องค์ประกอบอินพุตมีอยู่ในองค์ประกอบที่มีการใช้คำสั่งเปิด

นี่เป็นวิธีที่ฉันชอบมากที่สุด:

Directive:

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

ฉันหวังว่านี่จะช่วยให้ใครบางคนที่นั่น!


คุณเพิ่งสร้างสิ่งที่ html "label" ทำคุณสามารถแทนที่ "div focus-input" ด้วย "label" และกำจัดคำสั่ง
frst

1

ง่ายนิดเดียวลองดูสิ

HTML

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

จาวาสคริปต์

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

ถูกต้องจากบริบท แต่ไม่ใช่วิธีการทำ AngularJS
CodeMonkey

1

หากคุณต้องการตั้งสมาธิกับองค์ประกอบเฉพาะคุณสามารถใช้วิธีการด้านล่าง

  1. 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. ฉีดเข้าไปในคอนโทรลเลอร์จากตำแหน่งที่คุณต้องการโทร

  3. เรียกบริการนี้


0

ฉันคิดว่าคำสั่งนั้นไม่จำเป็น ใช้ HTML id และแอตทริบิวต์ class เพื่อเลือกองค์ประกอบที่จำเป็นและให้บริการใช้ document.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 () เพื่อบังคับวงจรย่อย


0

เพียงแค่โยนกาแฟ

app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.