Skip to content

Commit 8b5ee63

Browse files
Optimize DOM traversal in i18n replacement using TreeWalker
Replaced `document.querySelectorAll('*')` with `document.createTreeWalker` to avoid creating a large NodeList and improve iteration performance. Optimized attribute iteration to use a standard for loop instead of `Array.from` to reduce memory allocation. Added `NodeFilter` to test globals to support TreeWalker testing. Co-authored-by: cmuench <211294+cmuench@users.noreply.github.com>
1 parent ca88c3d commit 8b5ee63

2 files changed

Lines changed: 18 additions & 9 deletions

File tree

tests/utils.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('replaceI18nPlaceholders', () => {
1111
dom = new JSDOM(`<!DOCTYPE html><html><head><title data-i18n="title"></title></head><body><span data-i18n="hello"></span><p>__MSG_world__</p></body></html>`);
1212
global.document = dom.window.document;
1313
global.Node = dom.window.Node;
14+
global.NodeFilter = dom.window.NodeFilter;
1415
global.browser = {
1516
i18n: {
1617
getMessage: (key) => ({ title: 'Title', hello: 'Hello', world: 'World' }[key])
@@ -21,6 +22,7 @@ describe('replaceI18nPlaceholders', () => {
2122
dom.window.close();
2223
delete global.document;
2324
delete global.Node;
25+
delete global.NodeFilter;
2426
delete global.browser;
2527
});
2628
test('replaces placeholders in elements and text nodes', () => {

utils/utils.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,24 @@ function replaceI18nPlaceholders() {
6565
});
6666

6767
// Replace __MSG_...__ patterns in common attributes (e.g., placeholder, title, aria-label)
68-
document.querySelectorAll('*').forEach(el => {
69-
if (!el.attributes) return;
70-
Array.from(el.attributes).forEach(attr => {
71-
if (typeof attr.value === 'string' && attr.value.includes('__MSG_')) {
72-
const newVal = replaceTokens(attr.value);
73-
if (newVal !== attr.value) {
74-
el.setAttribute(attr.name, newVal);
68+
if (document.documentElement) {
69+
const walker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT);
70+
let el = walker.currentNode;
71+
while (el) {
72+
if (el.hasAttributes()) {
73+
for (let i = 0; i < el.attributes.length; i++) {
74+
const attr = el.attributes[i];
75+
if (typeof attr.value === 'string' && attr.value.includes('__MSG_')) {
76+
const newVal = replaceTokens(attr.value);
77+
if (newVal !== attr.value) {
78+
el.setAttribute(attr.name, newVal);
79+
}
80+
}
7581
}
7682
}
77-
});
78-
});
83+
el = walker.nextNode();
84+
}
85+
}
7986

8087
// Replace __MSG_...__ patterns in the document title
8188
if (document.title && document.title.includes('__MSG_')) {

0 commit comments

Comments
 (0)