(rootNode: Node)
| 24 | |
| 25 | // 传入父节点,返回所有需要翻译的 DOM 元素数组 |
| 26 | export function grabAllNode(rootNode: Node): Element[] { |
| 27 | if (!rootNode) return []; |
| 28 | |
| 29 | const result: Element[] = []; |
| 30 | |
| 31 | const walker = document.createTreeWalker( |
| 32 | rootNode, |
| 33 | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, |
| 34 | { |
| 35 | acceptNode: (node: Node): number => { |
| 36 | if (node instanceof Text) return NodeFilter.FILTER_ACCEPT; |
| 37 | |
| 38 | if (!(node instanceof Element)) return NodeFilter.FILTER_SKIP; |
| 39 | |
| 40 | const tag = node.tagName.toLowerCase(); |
| 41 | |
| 42 | // 跳过黑名单标签 |
| 43 | if (skipSet.has(tag) || |
| 44 | node.classList?.contains('sr-only') || |
| 45 | node.classList?.contains('notranslate')) { |
| 46 | return NodeFilter.FILTER_REJECT; |
| 47 | } |
| 48 | |
| 49 | // 在初始全局翻译时 跳过header与footer |
| 50 | if (tag === 'header' || tag === 'footer') { |
| 51 | return NodeFilter.FILTER_REJECT; |
| 52 | } |
| 53 | |
| 54 | // 检查是否只包含有效文本内容 |
| 55 | let hasText = false; |
| 56 | let hasElement = false; |
| 57 | let hasNonEmptyElement = false; |
| 58 | |
| 59 | for (const child of node.childNodes) { |
| 60 | if (child.nodeType === Node.ELEMENT_NODE) { |
| 61 | hasElement = true; |
| 62 | // 检查子元素是否包含文本 |
| 63 | if (child.textContent?.trim()) { |
| 64 | hasNonEmptyElement = true; |
| 65 | } |
| 66 | } |
| 67 | if (child.nodeType === Node.TEXT_NODE && child.textContent?.trim()) { |
| 68 | hasText = true; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // 如果有非空子元素,跳过当前节点 |
| 73 | if (hasNonEmptyElement) { |
| 74 | return NodeFilter.FILTER_SKIP; |
| 75 | } |
| 76 | |
| 77 | if (hasText && !hasElement) { |
| 78 | return NodeFilter.FILTER_ACCEPT; |
| 79 | } |
| 80 | |
| 81 | // 如果有子元素,继续遍历 |
| 82 | if (node.childNodes.length > 0) { |
| 83 | return NodeFilter.FILTER_SKIP; |
no test coverage detected