diff --git a/src/modal/modal.js b/src/modal/modal.js index c13d154bfe..43b27d2391 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -105,8 +105,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) } }]) - .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', - function($modalStack, $q, $animate, $animateCss) { + .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document', + function($modalStack, $q, $animate, $animateCss, $document) { return { scope: { index: '@' @@ -172,19 +172,25 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) $q.when(animationPromise).then(function() { - var inputWithAutofocus = element[0].querySelector('[autofocus]'); /** - * Auto-focusing of a freshly-opened modal element causes any child elements - * with the autofocus attribute to lose focus. This is an issue on touch - * based devices which will show and then hide the onscreen keyboard. - * Attempts to refocus the autofocus element via JavaScript will not reopen - * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus - * the modal element if the modal does not contain an autofocus element. + * If something within the freshly-opened modal already has focus (perhaps via a + * directive that causes focus). then no need to try and focus anything. */ - if (inputWithAutofocus) { - inputWithAutofocus.focus(); - } else { - element[0].focus(); + if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } } }); diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index e708176961..d6f1ac935f 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -22,6 +22,12 @@ describe('$uibModal', function () { scope.text = ctrl.text; } }; + }).directive('focusMe', function() { + return { + link: function(scope, elem, attrs) { + elem.focus(); + } + }; }); })); @@ -413,6 +419,24 @@ describe('$uibModal', function () { openAndCloseModalWithAutofocusElement(); }); + it('should not focus on the element that has autofocus attribute when the modal is opened and something in the modal already has focus and the animations have finished', function() { + function openAndCloseModalWithAutofocusElement() { + + var modal = open({template: '
'}); + $animate.flush(); + $rootScope.$digest(); + expect(angular.element('#auto-focus-element')).not.toHaveFocus(); + expect(angular.element('#pre-focus-element')).toHaveFocus(); + + close(modal, 'closed ok'); + + expect(modal.result).toBeResolvedWith('closed ok'); + } + + openAndCloseModalWithAutofocusElement(); + openAndCloseModalWithAutofocusElement(); + }); + it('should wait until the in animation is finished before attempting to focus the modal or autofocus element', function() { function openAndCloseModalWithAutofocusElement() { var modal = open({template: ''}); @@ -846,7 +870,7 @@ describe('$uibModal', function () { expect($document.find('.modal-backdrop')).not.toHaveClass('fade'); }); }); - + describe('appendTo', function() { it('should be added to body by default', function() { var modal = open({template: '