modal_handler.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. class ModalHandler {
  2. static exists() {
  3. return document.getElementById("modal-container") !== null;
  4. }
  5. static getModalContainer() {
  6. let container = document.getElementById("modal-container");
  7. if (container === undefined) {
  8. return;
  9. }
  10. return container;
  11. }
  12. static getFocusableElements() {
  13. let container = this.getModalContainer();
  14. if (container === undefined) {
  15. return;
  16. }
  17. return container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
  18. }
  19. static setupFocusTrap() {
  20. let focusableElements = this.getFocusableElements();
  21. if (focusableElements === undefined) {
  22. return;
  23. }
  24. let firstFocusableElement = focusableElements[0];
  25. let lastFocusableElement = focusableElements[focusableElements.length - 1];
  26. this.getModalContainer().onkeydown = (e) => {
  27. if (e.key !== 'Tab') {
  28. return;
  29. }
  30. // If there is only one focusable element in the dialog we always want to focus that one with the tab key.
  31. // This handles the special case of having just one focusable element in a dialog where keyboard focus is placed on an element that is not in the tab order.
  32. if (focusableElements.length === 1) {
  33. firstFocusableElement.focus();
  34. e.preventDefault();
  35. return;
  36. }
  37. if (e.shiftKey && document.activeElement === firstFocusableElement) {
  38. lastFocusableElement.focus();
  39. e.preventDefault();
  40. } else if (!e.shiftKey && document.activeElement === lastFocusableElement) {
  41. firstFocusableElement.focus();
  42. e.preventDefault();
  43. }
  44. };
  45. }
  46. static open(fragment, initialFocusElementId) {
  47. if (ModalHandler.exists()) {
  48. return;
  49. }
  50. this.activeElement = document.activeElement;
  51. let container = document.createElement("div");
  52. container.id = "modal-container";
  53. container.setAttribute("role", "dialog");
  54. container.appendChild(document.importNode(fragment, true));
  55. document.body.appendChild(container);
  56. let closeButton = document.querySelector("button.btn-close-modal");
  57. if (closeButton !== null) {
  58. closeButton.onclick = (event) => {
  59. event.preventDefault();
  60. ModalHandler.close();
  61. };
  62. }
  63. let initialFocusElement;
  64. if (initialFocusElementId !== undefined) {
  65. initialFocusElement = document.getElementById(initialFocusElementId);
  66. } else {
  67. initialFocusElement = this.getFocusableElements()[0];
  68. }
  69. initialFocusElement.focus();
  70. this.setupFocusTrap();
  71. }
  72. static close() {
  73. let container = this.getModalContainer();
  74. if (container !== null) {
  75. container.parentNode.removeChild(container);
  76. }
  77. if (this.activeElement !== undefined) {
  78. this.activeElement.focus();
  79. }
  80. }
  81. }