utils.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. function mapTags(tag) {
  2. let ret = ''
  3. switch (tag) {
  4. case 'A':
  5. ret = 'Link: '
  6. break
  7. case 'BUTTON':
  8. ret = 'Button: '
  9. break
  10. case 'IMG':
  11. ret = 'Image: '
  12. break
  13. case 'INPUT':
  14. ret = 'Textbox: '
  15. break
  16. case 'TEXTAREA':
  17. ret = 'Textbox: '
  18. break
  19. default:
  20. ret = 'Text: '
  21. break
  22. }
  23. return ret
  24. }
  25. function extractTextForMagnify(e) {
  26. let meaningfulNode = e.path[0]
  27. while (!meaningfulNode.getAttribute || !meaningfulNode.getAttribute('tabindex')) {
  28. meaningfulNode = meaningfulNode.parentNode
  29. if (!meaningfulNode) {
  30. return
  31. }
  32. }
  33. if (e.type === 'mouseover' && (
  34. meaningfulNode.getAttribute('data-aria-navigation-area') !== null ||
  35. meaningfulNode.getAttribute('data-aria-viewport-area') !== null ||
  36. meaningfulNode.getAttribute('data-aria-interaction-area') !== null
  37. )
  38. ) {
  39. return
  40. }
  41. let elemType = ''
  42. const ariaLabel = meaningfulNode.getAttribute('aria-label')
  43. if (ariaLabel !== null) {
  44. elemType = ariaLabel
  45. } else {
  46. elemType = mapTags(meaningfulNode.tagName)
  47. }
  48. let elemDisc = ''
  49. const ariaDescription = meaningfulNode.getAttribute('aria-description')
  50. if (ariaDescription !== null) {
  51. elemDisc = ariaDescription
  52. } else {
  53. elemDisc = meaningfulNode.innerText
  54. }
  55. return {
  56. elemType,
  57. elemDisc
  58. }
  59. }
  60. function isObject(p) {
  61. return Object.prototype.toString.call(p) === '[object Object]'
  62. }
  63. // 判断两个对象内容是否相同
  64. function isSameObject(object1, object2) {
  65. const keys1 = Object.keys(object1)
  66. const keys2 = Object.keys(object2)
  67. if (keys1.length !== keys2.length) {
  68. return false
  69. }
  70. for (let index = 0; index < keys1.length; index++) {
  71. const val1 = object1[keys1[index]]
  72. const val2 = object2[keys2[index]]
  73. const areObjects = isObject(val1) && isObject(val2)
  74. if (
  75. (areObjects && !isSameObject(val1, val2)) ||
  76. (!areObjects && (val1 !== val2))
  77. ) {
  78. return false
  79. }
  80. }
  81. return true
  82. }
  83. function getAndFocusNextNodeWithCustomAttribute(attriName) {
  84. const startNode = (document.activeElement || document.body)
  85. const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT)
  86. treeWalker.currentNode = startNode
  87. let targetNode = null
  88. // eslint-disable-next-line
  89. while(true) {
  90. const nextNode = treeWalker.nextNode()
  91. if (!nextNode) {
  92. console.log('往下没找到')
  93. break
  94. }
  95. if (nextNode.dataset[attriName] !== undefined) {
  96. console.log('往下找到了')
  97. targetNode = nextNode
  98. break
  99. }
  100. }
  101. if (!targetNode && (startNode !== document.body)) {
  102. treeWalker.currentNode = document.body
  103. // eslint-disable-next-line
  104. while(true) {
  105. const nextNode = treeWalker.nextNode()
  106. if (!nextNode) {
  107. console.log('往上也没找到')
  108. break
  109. }
  110. if (nextNode.dataset[attriName] !== undefined) {
  111. console.log('往上找到了')
  112. targetNode = nextNode
  113. break
  114. }
  115. }
  116. }
  117. if (targetNode) {
  118. targetNode.focus()
  119. if (document.activeElement !== targetNode) {
  120. targetNode.setAttribute('tabindex', '0')
  121. targetNode.focus()
  122. }
  123. }
  124. return targetNode
  125. }
  126. function __focusNextFocusableNode(treeWalker) {
  127. // eslint-disable-next-line
  128. while(true) {
  129. const nextNode = treeWalker.nextNode()
  130. if (!nextNode) {
  131. return false
  132. }
  133. if (nextNode.focus) {
  134. nextNode.focus()
  135. if (document.activeElement === nextNode) {
  136. return true
  137. }
  138. }
  139. }
  140. }
  141. function iterateOnFocusableNode(startNode, focusedNodeHandler) {
  142. const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT)
  143. treeWalker.currentNode = startNode
  144. treeWalker.currentNode.focus()
  145. if (document.activeElement === treeWalker.currentNode) {
  146. // console.log('起始节点可以focus')
  147. } else {
  148. // console.log('起始节点不可以focus,focus到下一节点。')
  149. const ret = __focusNextFocusableNode(treeWalker)
  150. if (!ret) {
  151. return
  152. }
  153. }
  154. const iterator = () => {
  155. focusedNodeHandler(treeWalker.currentNode).then(() => {
  156. const result = __focusNextFocusableNode(treeWalker)
  157. if (result) {
  158. // console.log('遍历到下一个节点!')
  159. iterator()
  160. } else {
  161. // console.log('遍历结束!')
  162. }
  163. }).catch((e) => {
  164. // console.log('遍历中止!', e)
  165. })
  166. }
  167. iterator()
  168. }
  169. /**
  170. * 返回一个自带消抖效果的函数,用res表示。
  171. *
  172. * fn: 需要被消抖的函数
  173. * delay: 消抖时长
  174. * isImmediateCall: 是在第一次调用时立即执行fn,还是在最后一次调用后等delay时长再调用fn
  175. */
  176. function debounce(fn, delay, isImmediateCall = false) {
  177. let timer = null
  178. // 上次调用的时刻
  179. let lastCallTime = 0
  180. if (isImmediateCall) {
  181. return function (...args) {
  182. const context = this
  183. const currentTime = Date.now()
  184. if (currentTime - lastCallTime >= delay) {
  185. fn.apply(context, args)
  186. }
  187. lastCallTime = currentTime
  188. }
  189. } else {
  190. return function (...args) {
  191. if (timer) {
  192. clearTimeout(timer)
  193. }
  194. const context = this
  195. timer = setTimeout(() => {
  196. fn.apply(context, args)
  197. }, delay)
  198. }
  199. }
  200. }
  201. export default {
  202. mapTags,
  203. extractTextForMagnify,
  204. isSameObject,
  205. iterateOnFocusableNode,
  206. debounce,
  207. getAndFocusNextNodeWithCustomAttribute,
  208. }