patchFocus.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. 'use strict';
  2. var dispatchEvent = require('../event/dispatchEvent.js');
  3. require('../utils/dataTransfer/Clipboard.js');
  4. var getActiveElement = require('../utils/focus/getActiveElement.js');
  5. require('@testing-library/dom');
  6. const patched = Symbol('patched focus/blur methods');
  7. function patchFocus(HTMLElement) {
  8. if (HTMLElement.prototype[patched]) {
  9. return;
  10. }
  11. // eslint-disable-next-line @typescript-eslint/unbound-method
  12. const { focus, blur } = HTMLElement.prototype;
  13. Object.defineProperties(HTMLElement.prototype, {
  14. focus: {
  15. configurable: true,
  16. get: ()=>patchedFocus
  17. },
  18. blur: {
  19. configurable: true,
  20. get: ()=>patchedBlur
  21. },
  22. [patched]: {
  23. configurable: true,
  24. get: ()=>({
  25. focus,
  26. blur
  27. })
  28. }
  29. });
  30. let activeCall;
  31. function patchedFocus(options) {
  32. if (this.ownerDocument.visibilityState !== 'hidden') {
  33. return focus.call(this, options);
  34. }
  35. const blurred = getActiveTarget(this.ownerDocument);
  36. if (blurred === this) {
  37. return;
  38. }
  39. const thisCall = Symbol('focus call');
  40. activeCall = thisCall;
  41. if (blurred) {
  42. blur.call(blurred);
  43. dispatchEvent.dispatchDOMEvent(blurred, 'blur', {
  44. relatedTarget: this
  45. });
  46. dispatchEvent.dispatchDOMEvent(blurred, 'focusout', {
  47. relatedTarget: activeCall === thisCall ? this : null
  48. });
  49. }
  50. if (activeCall === thisCall) {
  51. focus.call(this, options);
  52. dispatchEvent.dispatchDOMEvent(this, 'focus', {
  53. relatedTarget: blurred
  54. });
  55. }
  56. if (activeCall === thisCall) {
  57. dispatchEvent.dispatchDOMEvent(this, 'focusin', {
  58. relatedTarget: blurred
  59. });
  60. }
  61. }
  62. function patchedBlur() {
  63. if (this.ownerDocument.visibilityState !== 'hidden') {
  64. return blur.call(this);
  65. }
  66. const blurred = getActiveTarget(this.ownerDocument);
  67. if (blurred !== this) {
  68. return;
  69. }
  70. const thisCall = Symbol('blur call');
  71. activeCall = thisCall;
  72. blur.call(this);
  73. dispatchEvent.dispatchDOMEvent(blurred, 'blur', {
  74. relatedTarget: null
  75. });
  76. dispatchEvent.dispatchDOMEvent(blurred, 'focusout', {
  77. relatedTarget: null
  78. });
  79. }
  80. }
  81. function getActiveTarget(document) {
  82. const active = getActiveElement.getActiveElement(document);
  83. return (active === null || active === undefined ? undefined : active.tagName) === 'BODY' ? null : active;
  84. }
  85. function restoreFocus(HTMLElement) {
  86. if (HTMLElement.prototype[patched]) {
  87. const { focus, blur } = HTMLElement.prototype[patched];
  88. Object.defineProperties(HTMLElement.prototype, {
  89. focus: {
  90. configurable: true,
  91. get: ()=>focus
  92. },
  93. blur: {
  94. configurable: true,
  95. get: ()=>blur
  96. }
  97. });
  98. }
  99. }
  100. exports.patchFocus = patchFocus;
  101. exports.restoreFocus = restoreFocus;