17

What is the best way, when hitting enter inside a form, the focus to go to the next input instead submitting the form with angularjs.

I have a form with a lot of fields and customers are used to hit enter to move to the next input (comming from desktop applications). The angularjs saves the form when the user hits enter. I like to change this. Is it possible ?

darpet
  • 3,073
  • 3
  • 33
  • 40

10 Answers10

13

I suggest making a custom directive. Something like this. I haven't tested this.

.directive('focus', function() {
  return {
    restrict: 'A',
    link: function($scope,elem,attrs) {

      elem.bind('keydown', function(e) {
        var code = e.keyCode || e.which;
        if (code === 13) {
          e.preventDefault();
          elem.next().focus();
        }
      });
    }
  }
});

Something like that should work. You might have to tweek something. Good luck.

Zack Argyle
  • 8,057
  • 4
  • 29
  • 37
  • Thanks I will try. I need to find a way how to prevent form submit when hitting enter (yet do not know if your code prevents this) – darpet Aug 06 '13 at 18:07
  • 1
    I edited it and it works perfectly. I tested it out. Bam. Just stick the word "focus" in any input elements you want to work that way. – Zack Argyle Aug 06 '13 at 18:14
  • 5
    One change, use this for a more usable version. elem.nextAll('input').first().focus(); – Zack Argyle Aug 06 '13 at 21:02
  • When this answer was written those were built in Angular.Element methods. Easy enough to change to addEvenyListener and nextSibling – Zack Argyle Apr 07 '16 at 13:52
11

Create a custom directive:

.directive('nextOnEnter', function () {
    return {
        restrict: 'A',
        link: function ($scope, selem, attrs) {
            selem.bind('keydown', function (e) {
                var code = e.keyCode || e.which;
                if (code === 13) {
                    e.preventDefault();
                    var pageElems = document.querySelectorAll('input, select, textarea'),
                        elem = e.srcElement || e.target,
                        focusNext = false,
                        len = pageElems.length;
                    for (var i = 0; i < len; i++) {
                        var pe = pageElems[i];
                        if (focusNext) {
                            if (pe.style.display !== 'none') {
                                angular.element(pe).focus();
                                break;
                            }
                        } else if (pe === elem) {
                            focusNext = true;
                        }
                    }
                }
            });
        }
    }
})
Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
  • 1
    Had to make a couple tweaks. One: `e.srcElement` is specific to old IE so `e.srcElement || e.target` works instead. `pe === e.srcElement` needs to be `pe === elem` and `pe.focus()` becomes `angular.element(pe).focus()` – c0bra Jul 11 '17 at 14:13
  • Awesome. This works. My only problem is that it does not work with select / md-select. Can we do something about it? – systemdebt Dec 18 '17 at 10:40
  • Not bad.. but it doesn't work with tabindex property. But it work better than other answer. – Francois Girard Jan 24 '18 at 15:08
4

This is the directive I ended up with (thanks to Zack Argyle):


    
    angular.module('myApp').directive("nextFocus", nextFocus);

    /** Usage:
      <input next-focus id="field1">
      <input next-focus id="field2">
      <input id="field3">
      Upon pressing ENTER key the directive will switch focus to
      the next field id e.g field2
      The last field should not have next-focus directive to avoid
      focusing on non-existing element.
      Works for Web, iOS (Go button) & Android (Next button) browsers, 
    **/

    function nextFocus() {
      var directive = {
        restrict: 'A',
        link: function(scope, elem, attrs) {
          elem.bind('keydown', function(e) {
            var partsId = attrs.id.match(/field(\d{1})/);
            var currentId = parseInt(partsId[1]);

            var code = e.keyCode || e.which;
            if (code === 13) {
              e.preventDefault();
              document.querySelector('#field' + (currentId + 1)).focus();
            }
          });
        }
      };
      return directive;

    }
Olegdater
  • 2,381
  • 22
  • 20
3

I tried this solution out. As advertised, it needed some tweaking. Here is what ended up working for me:

.directive("focus", function () {
        return {
            restrict: "A",
            link: function ($scope, elem, attrs) {
                var focusables = $(":focusable");
                elem.bind("keydown", function (e) {
                    var code = e.keyCode || e.which;
                    if (code === 13) {
                        var current = focusables.index(this);
                        var next = focusables.eq(current + 1).length ? focusables.eq(current + 1) : focusables.eq(0);
                        next.focus();
                        e.preventDefault();
                    }
                });
            }
        }

Note that the in order to get the :focusable pseudo to work, you will need to reference JQueryUI. (the latest version 1.11.4 worked for me)

lipidfish
  • 356
  • 4
  • 11
1

This is the directive I ended up with (thanks to Zack Argyle and Oleg):

app.directive("nextFocus", function () {

    /** Usage:
      <input next-focus tabindex="0" id="field1">
      <input next-focus tabindex="1" id="field2">
      <input id="field3">
      Upon pressing ENTER key the directive will switch focus to
      the next field id e.g field2
      The last field should not have next-focus directive to avoid
      focusing on non-existing element.
      Works for Web, iOS (Go button) & Android (Next button) browsers, 
    **/
    var directive = {
        restrict: 'A',
        link: function (scope, elem, attrs) {
            elem.bind('keydown', function (e) {
                var code = e.keyCode || e.which;
                if (code === 13) {
                    try {
                        if (attrs.tabindex != undefined) {
                            var currentTabIndex = attrs.tabindex;
                            var nextTabIndex = parseInt(attrs.tabindex) + 1;
                            $("[tabindex=" + nextTabIndex + "]").focus();
                        }
                    } catch (e) {

                    }
                }
            });
        }
    };
    return directive;

});
wolcy97
  • 21
  • 2
1

Based on the answer by wolcy97 but using only angular

 /** Usage:
  <input next-focus tabindex="0">
  <input next-focus tabindex="1">
  <input tabindex="2">
  Upon pressing ENTER key the directive will switch focus to
  the next tabindex.
  The last field should not have next-focus directive to avoid
  focusing on non-existing element.
  Works for Web, iOS (Go button) & Android (Next button) browsers, 
**/ 
 app.directive('nextFocus', [function() {
  return {
    restrict: 'A',
    link: function(scope, elem, attrs) {
      elem.bind('keydown', function(e) {
        var code = e.keyCode || e.which;
        if (code === 13) {
          e.preventDefault();
          try {
            if (attrs.tabindex !== undefined) {
              var currentTabeIndex = attrs.tabindex;
              var nextTabIndex = parseInt(currentTabeIndex) + 1;
              var elems = document.querySelectorAll("[tabindex]");
              for (var i = 0, len = elems.length; i < len; i++) {
                var el = angular.element(elems[i]);
                var idx = parseInt(el.attr('tabindex'));
                if (idx === nextTabIndex) {
                  elems[i].focus();
                  break;
                }
              }
            }
          } catch (e) {
            console.log('Focus error: ' + e);
          }
        }
      });
    }
  };
}]);
jecxjo
  • 174
  • 1
  • 7
0

Pure JavaScript Enter as TAB

angular.module('app').directive('tabNext', function () {
return {
    restrict: 'A',
    link: function (scope, elem) {

        elem.bind('keyup', function (e) {
            var code = e.keyCode || e.which;
            if (code === 13) {
                e.preventDefault();
                var eIDX = -1;
                for (var i = 0; i < this.form.elements.length; i++) {
                    if (elem.eq(this.form.elements[i])) {
                         eIDX = i;
                         break;
                    }
                }
                if (eIDX === -1) {
                    return;
                }
                var j = eIDX + 1;
                var theform = this.form;
                while (j !== eIDX) {
                    if (j >= theform.elements.length){
                        j = 0;
                    }
                    if ((theform.elements[j].type !== "hidden") && (theform.elements[j].type !== "file")
                            && (theform.elements[j].name !== theform.elements[eIDX].name) 
                            && (! theform.elements[j].disabled) 
                            && (theform.elements[j].tabIndex >= 0)) {
                        if (theform.elements[j].type === "select-one") {
                            theform.elements[j].focus();
                        } else if (theform.elements[j].type === "button") {
                            theform.elements[j].focus();
                        } else {
                            theform.elements[j].focus();
                            theform.elements[j].select();
                        }
                        return;
                        break;
                    }
                    j++;
                }
            }
        });
    }
}});
0
  <table class="table table-striped table-bordered table-hover">
                                        <tr>
                                            <th>S No</th>
                                            <th>Stock Id</th>
                                            <th>Description</th>
                                            <th>Qty</th>
                                            <th>UOM</th>
                                            <th>Rate</th>
                                            <th>Amount</th>

                                            <th>Add</th>
                                            <th>Delete</th>
                                        </tr>
                                        <tr ng-repeat="item in stockitems">
                                            <td>{{$index + 1}}</td>
                                            <td>

                                                <input type="text" style="width:70px" id="stkid{{$index}}" class="form-control" name="stockid" required insert="Addnewrow();" ng-keyup="moveFocus('desc','amount','stkid','stkid',$index,$event)" ng-blur="getStockitem($index);" typeahead="a.stockitem_code as (a.stockitem_code +' | ' + a.stockitem_name +' | '+ a.rate) for a in stock | filter:$viewValue | limitTo:8" data-ng-model="item.stockid" rows="3" />
                                            </td>
                                            <td>

                                                <input type="text" class="form-control" id="desc{{$index}}" name="description" ng-keyup="moveFocus('quantity','stkid','desc','desc',$index,$event)" data-ng-model="item.description" rows="3" />
                                            </td>
                                            <td>

                                                <input type="text" style="width:70px" id="quantity{{$index}}" class="form-control" ng-keyup="moveFocus('uom','desc','quantity','quantity',$index,$event)" ng-change="GetAmount($index,'qty');" ng-pattern="/^\d+$/" required name="qty" data-ng-model="item.qty" rows="3" />
                                            </td>
                                            <td>

                                                <input type="text" style="width:70px" id="uom{{$index}}" class="form-control" name="uom" ng-keyup="moveFocus('rate','quantity','uom','uom',$index,$event)" data-ng-model="item.uom" required rows="3" />
                                            </td>
                                            <td>


                                                <input type="text" style="width:70px" id="rate{{$index}}" class="form-control" name="rate" ng-keyup="moveFocus('amount','uom','rate','rate',$index,$event)" required data-ng-model="item.rate" ng-pattern="/^\d{0,9}(\.\d{1,9})?$/" ng-change="GetAmount($index,'rate');" rows="3" />
                                            </td>
                                            <td>

                                                <input type="text" style="width:70px" id="amount{{$index}}" class="form-control" ng-keyup="moveFocus('stkid','rate','amount','amount',$index,$event)" name="amount" required data-ng-model="item.amount" rows="3" />
                                            </td>

                                            <td><span ng-click="AddEstimation($index);"><a>Add</a></span></td>
                                            <td><span ng-click="DeleterowEstimation($index);"><a>Delete</a></span></td>
                                        </tr>
                                    </table>
  • 2
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Nahuel Ianni Apr 04 '17 at 07:19
0
  $scope.moveFocus = function (nextId,prevId,downId,upId,index,event) {
            debugger;
            if (event.keyCode == 39) {
                nextId = nextId + index;
                $('#' + nextId).focus();
            }
            else if(event.keyCode == 37)
            {
                prevId = prevId + index;
                $('#' + prevId).focus();
            }
            else if(event.keyCode == 38)
            {
                upId = upId + (index - 1);
                $('#' + upId).focus();
            }
            else if(event.keyCode == 40)
            {
                downId = downId + (index + 1);
                $('#' + downId).focus();
            }
            else if(event.keyCode==13)
            {
                if (nextId == "desc") {
                    nextId = "quantity" + index;
                    $('#' + nextId).focus();
                }
                else if(nextId == "uom")
                {
                    nextId = "stkid" + (index + 1);
                    $('#' + nextId).focus();
                }
            }
        };
-1

On Enter press it moves to next element of DOM, but element requires id to set focus

starter.directive('focustonext', function () {
return {
    restrict: 'A',
    link: function ($scope, selem, attrs) {
        selem.bind('keydown', function (e) {
            var code = e.keyCode || e.which;
            if (code === 13) {
                e.preventDefault();
                var pageElems = document.querySelectorAll('input, select, textarea'),
                    elem = e.srcElement || e.target,
                    focusNext = false,
                    len = pageElems.length;
                for (var i = 0; i < len; i++) {
                    var pe = pageElems[i];
                    if (focusNext) {
                        if (pe.style.display !== 'none') {                               
                            document.getElementById(pe.id).focus();
                            break;
                        }
                    } else if (pe === elem) {
                        focusNext = true;
                    }
                }
            }
        });
    }
   }
});

Thanks all..

coder
  • 8,346
  • 16
  • 39
  • 53