utils.js 5.5 KB

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