cefeb02649c98fe91c12afbe44672e78e3d4eca060f3cf183e1744b2154a0f6b28015d530638667782f9e6f6b64643f6d73ea4f04f60a10656435003694ba5 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import Utils from './aria-utils';
  2. /**
  3. * @constructor
  4. * @desc Dialog object providing modal focus management.
  5. *
  6. * Assumptions: The element serving as the dialog container is present in the
  7. * DOM and hidden. The dialog container has role='dialog'.
  8. *
  9. * @param dialogId
  10. * The ID of the element serving as the dialog container.
  11. * @param focusAfterClosed
  12. * Either the DOM node or the ID of the DOM node to focus when the
  13. * dialog closes.
  14. * @param focusFirst
  15. * Optional parameter containing either the DOM node or the ID of the
  16. * DOM node to focus when the dialog opens. If not specified, the
  17. * first focusable element in the dialog will receive focus.
  18. */
  19. var aria = aria || {};
  20. var tabEvent;
  21. aria.Dialog = function(dialog, focusAfterClosed, focusFirst) {
  22. this.dialogNode = dialog;
  23. if (this.dialogNode === null || this.dialogNode.getAttribute('role') !== 'dialog') {
  24. throw new Error('Dialog() requires a DOM element with ARIA role of dialog.');
  25. }
  26. if (typeof focusAfterClosed === 'string') {
  27. this.focusAfterClosed = document.getElementById(focusAfterClosed);
  28. } else if (typeof focusAfterClosed === 'object') {
  29. this.focusAfterClosed = focusAfterClosed;
  30. } else {
  31. this.focusAfterClosed = null;
  32. }
  33. if (typeof focusFirst === 'string') {
  34. this.focusFirst = document.getElementById(focusFirst);
  35. } else if (typeof focusFirst === 'object') {
  36. this.focusFirst = focusFirst;
  37. } else {
  38. this.focusFirst = null;
  39. }
  40. if (this.focusFirst) {
  41. this.focusFirst.focus();
  42. } else {
  43. Utils.focusFirstDescendant(this.dialogNode);
  44. }
  45. this.lastFocus = document.activeElement;
  46. tabEvent = (e) => {
  47. this.trapFocus(e);
  48. };
  49. this.addListeners();
  50. };
  51. aria.Dialog.prototype.addListeners = function() {
  52. document.addEventListener('focus', tabEvent, true);
  53. };
  54. aria.Dialog.prototype.removeListeners = function() {
  55. document.removeEventListener('focus', tabEvent, true);
  56. };
  57. aria.Dialog.prototype.closeDialog = function() {
  58. this.removeListeners();
  59. if (this.focusAfterClosed) {
  60. setTimeout(() => {
  61. this.focusAfterClosed.focus();
  62. });
  63. }
  64. };
  65. aria.Dialog.prototype.trapFocus = function(event) {
  66. if (Utils.IgnoreUtilFocusChanges) {
  67. return;
  68. }
  69. if (this.dialogNode.contains(event.target)) {
  70. this.lastFocus = event.target;
  71. } else {
  72. Utils.focusFirstDescendant(this.dialogNode);
  73. if (this.lastFocus === document.activeElement) {
  74. Utils.focusLastDescendant(this.dialogNode);
  75. }
  76. this.lastFocus = document.activeElement;
  77. }
  78. };
  79. export default aria.Dialog;