focus.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  2. import isVisible from "./isVisible";
  3. function focusable(node) {
  4. var includePositive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  5. if (isVisible(node)) {
  6. var nodeName = node.nodeName.toLowerCase();
  7. var isFocusableElement =
  8. // Focusable element
  9. ['input', 'select', 'textarea', 'button'].includes(nodeName) ||
  10. // Editable element
  11. node.isContentEditable ||
  12. // Anchor with href element
  13. nodeName === 'a' && !!node.getAttribute('href');
  14. // Get tabIndex
  15. var tabIndexAttr = node.getAttribute('tabindex');
  16. var tabIndexNum = Number(tabIndexAttr);
  17. // Parse as number if validate
  18. var tabIndex = null;
  19. if (tabIndexAttr && !Number.isNaN(tabIndexNum)) {
  20. tabIndex = tabIndexNum;
  21. } else if (isFocusableElement && tabIndex === null) {
  22. tabIndex = 0;
  23. }
  24. // Block focusable if disabled
  25. if (isFocusableElement && node.disabled) {
  26. tabIndex = null;
  27. }
  28. return tabIndex !== null && (tabIndex >= 0 || includePositive && tabIndex < 0);
  29. }
  30. return false;
  31. }
  32. export function getFocusNodeList(node) {
  33. var includePositive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  34. var res = _toConsumableArray(node.querySelectorAll('*')).filter(function (child) {
  35. return focusable(child, includePositive);
  36. });
  37. if (focusable(node, includePositive)) {
  38. res.unshift(node);
  39. }
  40. return res;
  41. }
  42. var lastFocusElement = null;
  43. /** @deprecated Do not use since this may failed when used in async */
  44. export function saveLastFocusNode() {
  45. lastFocusElement = document.activeElement;
  46. }
  47. /** @deprecated Do not use since this may failed when used in async */
  48. export function clearLastFocusNode() {
  49. lastFocusElement = null;
  50. }
  51. /** @deprecated Do not use since this may failed when used in async */
  52. export function backLastFocusNode() {
  53. if (lastFocusElement) {
  54. try {
  55. // 元素可能已经被移动了
  56. lastFocusElement.focus();
  57. /* eslint-disable no-empty */
  58. } catch (e) {
  59. // empty
  60. }
  61. /* eslint-enable no-empty */
  62. }
  63. }
  64. export function limitTabRange(node, e) {
  65. if (e.keyCode === 9) {
  66. var tabNodeList = getFocusNodeList(node);
  67. var lastTabNode = tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1];
  68. var leavingTab = lastTabNode === document.activeElement || node === document.activeElement;
  69. if (leavingTab) {
  70. var target = tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0];
  71. target.focus();
  72. e.preventDefault();
  73. }
  74. }
  75. }