Skip to content

Instantly share code, notes, and snippets.

@ultrox
Created August 9, 2025 14:00
Show Gist options
  • Save ultrox/c4e16e4024a6289a149d2b685cd7ef5d to your computer and use it in GitHub Desktop.
Save ultrox/c4e16e4024a6289a149d2b685cd7ef5d to your computer and use it in GitHub Desktop.
Ai -> Ajax
const TARGET_WORD = "Ajax";
function* walkTextNodes(root, limit = Infinity) {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(n) {
const parentName = n.parentNode?.nodeName;
if (
parentName &&
/^(SCRIPT|STYLE|NOSCRIPT|TEXTAREA|INPUT)$/i.test(parentName)
) {
return NodeFilter.FILTER_REJECT;
}
return /\bai\b/i.test(n.nodeValue)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP;
}
});
let count = 0;
let current;
// Never unbounded while, you learn this by injecting sweat into chair
while (count < limit && (current = walker.nextNode())) {
yield current;
count++;
}
}
// Replace text in a single text node
function replaceInNode(textNode) {
textNode.nodeValue = textNode.nodeValue.replace(/\bai\b/gi, TARGET_WORD);
}
// Initial replacement on the existing page
for (const textNode of walkTextNodes(document.body)) {
replaceInNode(textNode);
}
// Watch for dynamic changes
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
mutation.addedNodes.forEach((added) => {
if (added.nodeType === Node.TEXT_NODE) {
replaceInNode(added);
} else if (added.nodeType === Node.ELEMENT_NODE) {
for (const t of walkTextNodes(added)) {
replaceInNode(t);
}
}
});
}
});
// Observe all additions under body, including nested children
observer.observe(document.body, { childList: true, subtree: true });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment