diff --git a/src/modal/modal.js b/src/modal/modal.js
index ddd924ff47..4e97d28b89 100644
--- a/src/modal/modal.js
+++ b/src/modal/modal.js
@@ -259,8 +259,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
};
//Modal focus behavior
- var focusableElementList;
- var focusIndex = 0;
var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
'iframe, object, embed, *[tabindex], *[contenteditable=true]';
@@ -391,15 +389,15 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
break;
}
case 9: {
- $modalStack.loadFocusElementList(modal);
+ var list = $modalStack.loadFocusElementList(modal);
var focusChanged = false;
if (evt.shiftKey) {
- if ($modalStack.isFocusInFirstItem(evt) || $modalStack.isModalFocused(evt, modal)) {
- focusChanged = $modalStack.focusLastFocusableElement();
+ if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) {
+ focusChanged = $modalStack.focusLastFocusableElement(list);
}
} else {
- if ($modalStack.isFocusInLastItem(evt)) {
- focusChanged = $modalStack.focusFirstFocusableElement();
+ if ($modalStack.isFocusInLastItem(evt, list)) {
+ focusChanged = $modalStack.focusFirstFocusableElement(list);
}
}
@@ -407,6 +405,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
evt.preventDefault();
evt.stopPropagation();
}
+
break;
}
}
@@ -476,8 +475,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
openedWindows.top().value.modalDomEl = angularDomEl;
openedWindows.top().value.modalOpener = modalOpener;
-
- $modalStack.clearFocusListCache();
};
function broadcastClosing(modalWindow, resultOrReason, closing) {
@@ -524,16 +521,17 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
}
};
- $modalStack.focusFirstFocusableElement = function() {
- if (focusableElementList.length > 0) {
- focusableElementList[0].focus();
+ $modalStack.focusFirstFocusableElement = function(list) {
+ if (list.length > 0) {
+ list[0].focus();
return true;
}
return false;
};
- $modalStack.focusLastFocusableElement = function() {
- if (focusableElementList.length > 0) {
- focusableElementList[focusableElementList.length - 1].focus();
+
+ $modalStack.focusLastFocusableElement = function(list) {
+ if (list.length > 0) {
+ list[list.length - 1].focus();
return true;
}
return false;
@@ -549,32 +547,25 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
return false;
};
- $modalStack.isFocusInFirstItem = function(evt) {
- if (focusableElementList.length > 0) {
- return (evt.target || evt.srcElement) === focusableElementList[0];
+ $modalStack.isFocusInFirstItem = function(evt, list) {
+ if (list.length > 0) {
+ return (evt.target || evt.srcElement) === list[0];
}
return false;
};
- $modalStack.isFocusInLastItem = function(evt) {
- if (focusableElementList.length > 0) {
- return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1];
+ $modalStack.isFocusInLastItem = function(evt, list) {
+ if (list.length > 0) {
+ return (evt.target || evt.srcElement) === list[list.length - 1];
}
return false;
};
- $modalStack.clearFocusListCache = function() {
- focusableElementList = [];
- focusIndex = 0;
- };
-
$modalStack.loadFocusElementList = function(modalWindow) {
- if (focusableElementList === undefined || !focusableElementList.length) {
- if (modalWindow) {
- var modalDomE1 = modalWindow.value.modalDomEl;
- if (modalDomE1 && modalDomE1.length) {
- focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
- }
+ if (modalWindow) {
+ var modalDomE1 = modalWindow.value.modalDomEl;
+ if (modalDomE1 && modalDomE1.length) {
+ return modalDomE1[0].querySelectorAll(tababbleSelector);
}
}
};
diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js
index 43775634dc..929ce5241f 100644
--- a/src/modal/test/modal.spec.js
+++ b/src/modal/test/modal.spec.js
@@ -715,6 +715,52 @@ describe('$uibModal', function() {
initialPage.remove();
});
+
+ it('should change focus to next proper element when DOM changes and tab is pressed', function() {
+ var initialPage = angular.element('Outland link');
+ angular.element(document.body).append(initialPage);
+ initialPage.focus();
+
+ open({
+ template:'abc' +
+ '',
+ keyboard: false
+ });
+ $rootScope.$digest();
+ expect($document).toHaveModalsOpen(1);
+
+ $('#tab-focus-link3').focus();
+ expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');
+
+ $('#tab-focus-button').remove();
+ triggerKeyDown(angular.element(document.activeElement), 9, false);
+ expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');
+
+ initialPage.remove();
+ });
+
+ it('should change focus to next proper element when DOM changes and shift+tab is pressed', function() {
+ var initialPage = angular.element('Outland link');
+ angular.element(document.body).append(initialPage);
+ initialPage.focus();
+
+ open({
+ template:'abc' +
+ '',
+ keyboard: false
+ });
+ $rootScope.$digest();
+ expect($document).toHaveModalsOpen(1);
+
+ $('#tab-focus-link1').focus();
+ expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');
+
+ $('#tab-focus-button').remove();
+ triggerKeyDown(angular.element(document.activeElement), 9, true);
+ expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');
+
+ initialPage.remove();
+ });
});
describe('default options can be changed in a provider', function() {