jQuery 在 AngularJS 中轻松操作 dom - 单击按钮,然后将焦点设置到输入元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15518772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Easy dom manipulation in AngularJS - click a button, then set focus to an input element
提问by Alon
I have this angular code:
我有这个角度代码:
<div class="element-wrapper" ng-repeat="element in elements">
<div class="first-wrapper">
<div class="button" ng-click="doSomething(element,$event)">{{element.name}}</div>
</div>
<div class="second-wrapper">
<input type="text" value="{{element.value}}">
</div>
</div>
What I want to happen:when the user clicks the button - the input element will be focused.
我想要发生的事情:当用户单击按钮时 - 输入元素将被聚焦。
How do I find the input element after I click the button element and focus it?
单击按钮元素并聚焦后如何找到输入元素?
I can do a function that looks like this:
我可以执行如下所示的函数:
function doSomething(element,$event) {
//option A - start manipulating in the dark:
$event.srcElement.parentNode.childNodes[1]
//option B - wrapping it with jQuery:
$($event.srcElement).closest('.element-wrapper').find('input').focus();
}
Neither of them work - Is there a nicer Angular way to do it? Using functions such as .closest()
and .find()
as in jQuery?
它们都不起作用 - 有更好的 Angular 方法吗?在 jQuery 中使用.closest()
和.find()
as 之类的函数?
Update:
更新:
I found this hack to be working (but it still doesn't seem like the correct solution):
我发现这个 hack 有效(但它似乎仍然不是正确的解决方案):
function doSomething(element,$event) {
setTimeout(function(){
$($event.srcElement).closest('.element-wrapper').find('input').focus();
},0)
}
I am wrapping it with setTimeout so after Angular finishes all of its manipulations it focuses on the input element.
我用 setTimeout 包装它,所以在 Angular 完成所有操作后,它专注于输入元素。
回答by Mark Rajcok
DOM manipulation should be in a directive instead of the controller. I would define a focusInput
directive and use it on the button:
DOM 操作应该在指令中而不是在控制器中。我会定义一个focusInput
指令并在按钮上使用它:
<div class="button" focus-input>{{element.name}}</div>
Directive:
指示:
app.directive('focusInput', function($timeout) {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
$timeout(function() {
element.parent().parent().find('input')[0].focus();
});
});
}
};
});
Since jqLiteis rather limited in terms of DOM traversal methods, I had to use parent().parent()
. You may wish to use jQuery or some JavaScript methods.
由于jqLite在 DOM 遍历方法方面相当有限,我不得不使用parent().parent()
. 您可能希望使用 jQuery 或一些 JavaScript 方法。
As you already found out, $timeout
is needed so that the focus()
method is called after the browser renders (i.e., finishes handling the click event).
正如您已经发现的那样,$timeout
需要focus()
在浏览器呈现后调用该方法(即,完成单击事件的处理)。
find('input')[0]
gives us access to the DOM element, allowing us to use the JavaScript focus()
method (rather than find('input').focus()
which would require jQuery).
find('input')[0]
允许我们访问 DOM 元素,允许我们使用 JavaScriptfocus()
方法(而不是find('input').focus()
需要 jQuery)。
回答by Sly_cardinal
I've been having a look at AngularJS recently and came across a similar situation.
我最近一直在看 AngularJS 并遇到了类似的情况。
I was working to update the Todo example application from the main angular page to add an "edit" mode when you double click on a todo item.
我正在努力从主角度页面更新 Todo 示例应用程序,以在您双击 todo 项目时添加“编辑”模式。
I was able to solve my issue using a model/state-based approach. If your application works in a similar way (you want to set focus on a field when some condition on the model is true) then this might work for you too.
我能够使用基于模型/状态的方法解决我的问题。如果您的应用程序以类似的方式工作(当模型的某些条件为真时,您希望将焦点设置在某个字段上),那么这也可能对您有用。
My approach is to set the model.editing
property to true
when the user double-clicks on the todo label - this shows the editable input and hides the regular non-editable label and checkbox.
We also have a custom directive called focusInput
that has a watch on the same model.editing
property and will set focus on the text field when the value changes:
我的方法是将model.editing
属性设置为true
用户双击待办事项标签时 - 这将显示可编辑输入并隐藏常规的不可编辑标签和复选框。我们还有一个名为的自定义指令focusInput
,它在同一model.editing
属性上有一个监视,并在值更改时将焦点设置在文本字段上:
<li ng-repeat="todo in todos">
<div>
<!-- Regular display view. -->
<div ng-show="todo.editing == false">
<label class="done-{{todo.done}}" ng-dblclick="model.editing = true">
<input type="checkbox" ng-model="todo.done"/>{{todo.text}}
</label>
</div>
<!-- Editable view. -->
<div ng-show="todo.editing == true">
<!--
- Add the `focus-input` directive with the statement "todo.editing == true".
This is the element that will receive focus when the statement evaluates to true.
- We also add the `todoBlur` directive so we can cancel editing when the text field loses focus.
-->
<input type="text" ng-model="todo.text" focus-input="todo.editing == true" todo-blur="todo.editing = false"/>
</div>
</div>
</li>
Here is the focusInput
directive that will set focus on the current element when some condition evaluates to true
:
这是focusInput
当某些条件评估为时将焦点设置在当前元素上的指令true
:
angular.module('TodoModule', [])
// Define a new directive called `focusInput`.
.directive('focusInput', function($timeout){
return function(scope, element, attr){
// Add a watch on the `focus-input` attribute.
// Whenever the `focus-input` statement changes this callback function will be executed.
scope.$watch(attr.focusInput, function(value){
// If the `focus-input` statement evaluates to `true`
// then use jQuery to set focus on the element.
if (value){
$timeout(function(){
element.select();
});
}
});
};
})
// Here is the directive to raise the 'blur' event.
.directive('todoBlur', [
'$parse', function($parse){
return function(scope, element, attr){
var fn = $parse(attr['todoBlur']);
return element.on('blur', function(event){
return scope.$apply(function(){
return fn(scope, {
$event: event
});
});
});
};
}
]);
回答by Omer Leshem
Here is a directive that triggers a focus event on a target dom element:
这是在目标 dom 元素上触发焦点事件的指令:
AngularJs Directive:
AngularJs 指令:
app.directive('triggerFocusOn', function($timeout) {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
$timeout(function() {
var otherElement = document.querySelector('#' + attrs.triggerFocusOn);
if (otherElement) {
otherElement.focus();
}
else {
console.log("Can't find element: " + attrs.triggerFocusOn);
}
});
});
}
};
});
The html:
html:
<button trigger-focus-on="targetInput">Click here to focus on the other element</button>
<input type="text" id="targetInput">
A live example on Plunker
Plunker上的一个活生生的例子
回答by Kier Simmons
I had to create an account just to provide the easy answer.
我必须创建一个帐户才能提供简单的答案。
//Add a bool to your controller's scope that indicates if your element is focused
... //ellipsis used so I don't write the part you should know
$scope.userInputActivate = false;
...
//Add a new directive to your app stack
...
.directive('focusBool', function() {
return function(scope, element, attrs) {
scope.$watch(attrs.focusBool, function(value) {
if (value) $timeout(function() {element.focus();});
});
}
})
...
<!--Now that our code is watching for a scope boolean variable, stick that variable on your input element using your new directive, and manipulate that variable as desired.-->
...
<div class="button" ng-click="userInputActivate=true">...</div>
...
<input type="text" focus-Bool="userInputActivate">
...
Be sure to reset this variable when you aren't using the input. You can add an ng-blur directive easy enough to change it back, or another ng-click event that resets it to false. Setting it to false just gets it ready for next time. Here is an ng-blur directive example I found in case you have trouble finding one.
当您不使用输入时,请务必重置此变量。您可以轻松添加 ng-blur 指令以将其更改回来,或者另一个将其重置为 false 的 ng-click 事件。将它设置为 false 只是让它为下一次做好准备。这是我找到的一个 ng-blur 指令示例,以防您找不到。
.directive('ngBlur', ['$parse', function($parse) {
return function(scope, element, attr) {
var fn = $parse(attr['ngBlur']);
element.bind('blur', function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
}
}]);
回答by Florian Hehlen
Here is what I have come up with. I started with Mark Rajcok's solution above and then moved to make it easy to re-use. It's configurable and does not require any code in your controller. Focus is pure presentation aspect and should not require controller code
这是我想出的。我从上面的 Mark Rajcok 的解决方案开始,然后转向使其易于重用。它是可配置的,不需要您的控制器中的任何代码。焦点是纯粹的表示方面,不应该需要控制器代码
html:
html:
<div id="focusGroup">
<div>
<input type="button" value="submit" pass-focus-to="focusGrabber" focus-parent="focusGroup">
</div>
<div>
<input type="text" id="focusGrabber">
</div>
</div>
directive:
指示:
chariotApp.directive('passFocusTo', function ($timeout) {
return {
link: function (scope, element, attrs) {
element.bind('click', function () {
$timeout(function () {
var elem = element.parent();
while(elem[0].id != attrs.focusParent) {
elem = elem.parent();
}
elem.find("#"+attrs.passFocusTo)[0].focus();
});
});
}
};
});
assumption:
假设:
- Your giver and taker are close by.
- when using this multiple times on one page id's used are unique or give and taker are in an isolated branch of the DOM.
- 你的给予者和接受者就在附近。
- 当在一个页面上多次使用时,使用的 id 是唯一的,或者给予和接受者在 DOM 的一个独立分支中。
回答by Filipp Gaponenko
For using .closest() method I suggest that You apply prototype inheritance mechanism fox expand angular opportunities. Just like this:
对于使用 .closest() 方法,我建议您应用原型继承机制fox expand angular机会。像这样:
angular.element.prototype.closest = (parentClass)->
$this = this
closestElement = undefined
while $this.parent()
if $this.parent().hasClass parentClass
closestElement = $this.parent()
break
$this = $this.parent()
closestElement
Markup:
标记:
<span ng-click="removeNote($event)" class="remove-note"></span>
Usage:
用法:
$scope.removeNote = ($event)->
currentNote = angular.element($event.currentTarget).closest("content-list_item")
currentNote.remove()
回答by Jana Filipenská
To find input add it Id <input id="input{{$index}}" .. />
and pass ngRepeat index as parameter to function ng-click="doSomething(element,$event, $index)"
要查找输入,请添加 Id<input id="input{{$index}}" .. />
并将 ngRepeat 索引作为参数传递给函数ng-click="doSomething(element,$event, $index)"
<div class="element-wrapper" ng-repeat="element in elements">
<div class="first-wrapper">
<div class="button" ng-click="doSomething(element,$event, $index)">{{element.name}}</div>
</div>
<div class="second-wrapper">
<input id="input{{$index}}" type="text" value="{{element.value}}">
</div>
</div>
In function use $timeout
with zero delay to wait to the end of DOM rendering. Then input can be found by getElementById
in $timeout
function. Don't forget to add $timeout
to controller.
在函数中使用$timeout
零延迟等待 DOM 渲染结束。然后可以通过getElementById
in$timeout
函数找到输入。不要忘记添加$timeout
到控制器。
.controller("MyController", function ($scope, $timeout)
{
$scope.doSomething = function(element,$event, index) {
//option A - start manipulating in the dark:
$event.srcElement.parentNode.childNodes[1]
$timeout(function ()
{
document.getElementById("input" + index).focus();
});
}
});