Technology, Continued...

Ramblings about business, technology, development and my life

NAVIGATION - SEARCH

Yet another Angular focus directive

I’ve seen lots of Angular directives that set focus of an element based on its html name or id.  And I’ve seen a few other methods as well.  But I couldn’t find one that could handle these requirements:

  1. Keep trying to focus the element for a period of time (useful when there’s a delay in the element actually being displayed in the dom)
  2. If you put the attribute on multiple input elements, the first one would get the initial focus (useful if you have the first one conditionally displaying, and want to fall back to a second one if it isn’t)
  3. Allows you to conditionally have the next input field displayed on the page get the focus (useful if clicking a button on your page, for example, adds an input field you want focused)

So, I wrote the one you see below called, originally enough, anotherFocus. 

It should be added to an element with the attribute…

another-focus="domFocus"            

where, on your controller,

$scope.domFocus = { setFocus: true }

Then, after page is loaded and maybe you've focused an element, you can set $scope.domFocus.setFocus = true right before causing another input to be added with the directive on it.  The next element added with the directive would get the focus.

The directive as written will retry the focus call every 100ms for up to 2 seconds, but of course, you can always change that in the code.  If your page takes a while to load (mine sometimes takes half a second because of some processing it’s doing), this is crucial.

Hope this helps someone.

angular.module('app').directive('anotherFocus',
function ($timeout) {
    return {
        scope: {
            //should be set up like another-focus="domFocus"
            //where, on controller, $scope.domFocus = { setFocus: true }
            //then, after page is loaded an maybe you've focused an element, 
            //you can set $scope.domFocus.setFocus = true right before causing another
            //input to be added with the directive on it
            trigger: '=anotherFocus'
        },

        link: function (scope, element) {
            var attempts = 0;

            function timeout(ele) {
                ele.focus();

                //we're willing to try 20 times at 100ms for a total of 2 seconds
                //to wait for the element to be on the page and active
                if (ele != document.activeElement && attempts++ < 20)
                    $timeout(function () {
                        timeout(ele);
                    }, 100);
                //console.log('attempt' + attempts);
            }

            //start the initial attempt to focus this element
            scope.$watch('trigger', function (value) {
                if (value.setFocus) {
                    //stop future elements with directive from getting focus unless we turn on setFocus from controller again
                    value.setFocus = false;
                    timeout(element[0]);
                }
            });
        },

    };
}
);
Comments are closed