【问题标题】:How do I create and focus a new element in AngularJS?如何在 AngularJS 中创建和聚焦新元素?
【发布时间】:2017-08-21 18:34:51
【问题描述】:

我有一个动态输入数量的表单,由 AngularJS 控制。

<body ng-app="mainApp" ng-controller="CreatePollController" ng-init="init(3)">
    <form id="createPollForm">
        <input class="create-input" ng-repeat="n in questions" id="q_{{$index}}" name="q_{{$index}}" type="text" ng-keypress="createInputKeypress($event);"/>
        <a href="javascript:void(0);" ng-click="addQuestion()">Add Question</a>
    </form>
</body>

这是由以下角度代码控制的:

app.controller('CreatePollController', function($scope) {
    $scope.questions = [];

    $scope.init = function(numOfInputs){
        for(var i = 0; i < numOfInputs; i++){
            $scope.questions.push({
                "questionText":""
            });
        }
    };

    $scope.addQuestion = function(){
        $scope.questions.push({
            "questionText":""
        });
    };

    $scope.createInputKeypress = function(e){
        if(e.keyCode === 13){
            e.preventDefault();

            var idx = Number(e.target.id.replace("q_", ""));
            if(idx === this.questions.length - 1){
                this.addQuestion();
            }

            // Wait for angular update ????

            var nextId = "#q_" + (++idx);
            $(nextId).focus();
        }
    };
});

目前,当用户在关注文本输入时按下 Enter 键时,会调用 createInputKeypress 函数,并且浏览器会关注表单中的下一个输入。但是,如果您当前专注于表单中的最后一个元素,它会向questions 数组添加一个新问题,这将导致在 DOM 中生成另一个输入。

但是,当创建这个新元素时,focus() 调用不起作用。我怀疑这是因为 angular 没有立即添加新元素,因此尝试使用 jQuery 定位和聚焦新元素不起作用。

有没有办法等待 DOM 更新,然后聚焦新元素?

【问题讨论】:

    标签: javascript jquery html angularjs events


    【解决方案1】:

    您可能已经知道,javascript 是基于轮流的,这意味着浏览器会轮流(循环)执行 JS 代码。目前,在下一个 javascript 周期中准备回调的方法是通过使用我们希望在下一个周期中运行的代码设置一个回调,我们可以通过以0 毫秒为间隔调用 setTimeout 来做到这一点,这将强制在浏览器完成(从)当前回调之后,在下一个 javascript 回合中调用给定的回调。

    尽量保持简单,一个浏览器循环按给定顺序执行这些操作

    1. 脚本(JS 发生的地方)
    2. 渲染(HTML 和 DOM 渲染)
    3. 绘画(在窗口中绘制渲染的 DOM)
    4. 其他(内部浏览器的东西)

    看看这个例子:

    console.log(1);
    console.log(2);
    
    setTimeout(function () {
        console.log(3);
        console.log(4);
    }, 0);
    
    console.log(5);
    console.log(6);
    
    /** prints in the console
     * 1 - in the current JS turn 
     * 2 - in the current JS turn
     * 5 - in the current JS turn
     * 6 - in the current JS turn
     * 3 - in the next JS turn
     * 4 - in the next JS turn
     **/

    3 和 4 在 5 和 6 之后打印,即使知道没有间隔 (0) 在setTimeout 中,因为setTimeout 基本上准备仅在当前javascript 轮次完成后调用给定的回调。如果在下一回合,当前时间与回调绑定setTimeout指令的时间差小于时间间隔,传入setTimeout,回调不会被调用,会等待下一回合,该过程重复,直到时间间隔小于该差异,才调用回调!

    由于 AngularJS 是一个封装了我们所有代码的框架,角度更新通常发生在我们的代码执行之后,在每个 javascript 循环结束时,这意味着对 HTML 的角度更改只会发生在当前的 javascript 轮流完成。

    AngularJS还内置了一个超时服务,叫做$timeoutdifference between the native setTimeout and angular's $timeout service是最后一个服务函数,恰好调用了原生的setTimeout,带有一个angular的内部回调,这个回调在它的反过来,负责执行我们在$timeout 中传递的回调,然后确保我们在$scope 中所做的任何更改都会反映在其他地方!然而,由于在我们的例子中我们实际上并不想更新$scope,所以我们不需要使用这个服务,一个简单的setTimeout 恰好更有效!

    了解所有这些信息后,我们可以使用setTimeout 来解决我们的问题。像这样:

    $scope.createInputKeypress = function(e){
        if(e.keyCode === 13){
            e.preventDefault();
    
            var idx = Number(e.target.id.replace("q_", ""));
            if(idx === this.questions.length - 1){
                this.addQuestion();
            }
    
            // Wait for the next javascript turn
            setTimeout(function () {
                var nextId = "#q_" + (++idx);
                $(nextId).focus();
            }, 0);
        }
    };
    

    为了使其更具语义性,我们可以包装 setTimeout 逻辑 在具有更上下文化名称的函数中,例如 runAfterRender:

    function runAfterRender (callback) {
        setTimeout(function () {
            if (angular.isFunction(callback)) {
                callback();
            }
        }, 0);
    }
    

    现在我们可以使用这个函数在下一个 javascript 轮次中准备代码执行:

    app.controller('CreatePollController', function($scope) {
        // functions
        function runAfterRender (callback) {
            setTimeout(function () {
                if (angular.isFunction(callback)) {
                    callback();
                }
            }, 0);
        }
    
        // $scope
        $scope.questions = [];
    
        $scope.init = function(numOfInputs){
            for(var i = 0; i < numOfInputs; i++){
                $scope.questions.push({
                    "questionText":""
                });
            }
        };
    
        $scope.addQuestion = function(){
            $scope.questions.push({
                "questionText":""
            });
        };
    
        $scope.createInputKeypress = function(e){
            if(e.keyCode === 13){
                e.preventDefault();
    
                var idx = Number(e.target.id.replace("q_", ""));
                if(idx === this.questions.length - 1){
                    this.addQuestion();
                }
    
                runAfterRender(function () {
                    var nextId = "#q_" + (++idx);
                    $(nextId).focus();
                });
            }
        };
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-30
      • 2015-11-21
      • 2018-01-05
      • 2016-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多