Skip to content

Instantly share code, notes, and snippets.

@davidsargent
Last active November 10, 2016 06:08
Show Gist options
  • Save davidsargent/eee22ed385ce42a7fc1adbf74f70197c to your computer and use it in GitHub Desktop.
Save davidsargent/eee22ed385ce42a7fc1adbf74f70197c to your computer and use it in GitHub Desktop.
Angular 1 directive to auto-focus next input when current input becomes valid
{
'use strict';
app.directive('autoNext', autoNext);
///////////////
/**
* autoNext
*
* Should be placed on an <input>
*/
function autoNext() {
return {
restrict : 'A',
require : ['ngModel'],
link : link
};
// LINK Function
function link( $scope, $elem, attrs, ctrls ) {
const model = ctrls[0];
let focusInputCurrentAttempt = 0;
// CHECK if $elem is valid
// ( Without custom validation this will be valid after the first character entered )
$scope.next = function() {
return model.$viewValue && model.$valid;
};
// WATCH next and go to the next input if the current one is valid
$scope.$watch( $scope.next, function nextWatch( newVal ) {
if ( newVal ) {
focusInputCurrentAttempt = 0;
recurseToFocusInput($elem);
}
});
///////////////
/**
* From the target element, see if the next sibling element is an <input>
* If yes, focus it and return.
* If not, recursively go up its DOM parents until we find a 'next' DOM element
* with an <input> descendant to focus.
*
* NOTE: The amount of parent DOM traversal attempts is capped. This may be adjusted.
*
* @param {Object} $target - The element to search from
* @param {Number} maxAttempts - The max number of times to traverse the DOM and get parent elements
*/
function recurseToFocusInput( $target, maxAttempts = 4 ) {
// RETURN if max parent attempts have been met
// We don't want to traverse up the DOM past a logical amount of tries
if ( focusInputCurrentAttempt>=maxAttempts ) return;
// Store next <input>
// Either it is the immediate next sibling element
// or we need to grab the parent and see if its next sibling has an <input>
let $nextInput = $target.next('input').length
? $target.next('input')
: $target.parent().next('div').find('input').eq(0);
// If an <input> was found, focus it
if ( $nextInput.length ) {
$nextInput.focus();
}
else {
// Increment max-attempts counter
focusInputCurrentAttempt++;
// Get next parent element
$target = $target.parent();
// Re-run next pass
recurseToFocusInput($target, maxAttempts);
}
}
}
}
}
<!--
Assumes you have validation on the <input> elements,
otherwise it will focus the next element after typing
one character ( as the field will be valid ).
For example, if you had a 4 digit month field,
it would need to be invalid until length is 4.
At that point it would auto-focus the next field
when it becomes valid.
-->
<div>
<input type="text" auto-next ng-model="set.input1">
<input type="text" auto-next ng-model="set.input2">
</div>
<div>
<input type="text" auto-next ng-model="set.input3">
</div>
<div>
<div>
<input type="text" auto-next ng-model="set.input4">
</div>
</div>
<input type="text" auto-next ng-model="set.input5">
<input type="text" ng-model="set.input6">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment