So HTML is a tree structure, each element (a node) can have other elements beneath it, so take this:
<body>
<div>
<header>
<h1>The main title</h1>
</header>
<article>
<header>
<h2>Some other title</h2>
</header>
<div>
<p>Some text</p>
</div>
</article>
</div>
</body>
To get at "The main title"
AND "Some text"
AND "Some other title"
AND , you need to drill down and select each of those.
You can’t just replace the textContent
on <body>
, because <body>
doesn’t have any text content. The text content is body > div > header > h1 > TEXT
and body > div > article > header > h1 > TEXT
and body > div > article > div > p > TEXT
So going through the entire document – that’s going to be slow because you need to go though every single element seeing if it has text.
But, just to provide a solution, and try to explain what it’s doing, here is one possible way to do that. I’ve tried to use as little and as basic stuff as possible, but you are trying to hit every single element on a page, so unfortunately you do need to do a bit of checking and know what you’re checking for.
A functional version is here:
That’s just a very quick demo: the code in the demo is slightly more complicated than the code posted here because I provided a way to reset, and had the text where changes happened highlight in red when it happens.
The code here should be enough for a basic usecase:
Anyway:
/**
* @param {string|RegExp} targetText -
* The text you're trying to target - can be a string, or a regex pattern
* @param {string} replacementText -
* The string you want to replace the target with.
* @param {HTMLElement} [root=HTMLBodyElement] -
* The element you want to target (by default the body)
*
* NOTE: the `root = document.querySelector("body") means
* "if there is no argument for `root` provided, use this
* default value instead"
*/
function htmlTextSwapper(targetText, replacementText, root = document.querySelector("body")) {
// Ensure the target is a regex:
let targetRegex = new RegExp(targetText);
// `NodeIterator` is an object provided by the browser that helps
// you to write code that walks over HTML nodes.
// The function used to create it takes a "root" HTML element
// and paramer called "whatToShow".
// In this case, for "whatToShow", `SHOW_TEXT` says "just
// show text nodes".
//
// see: https://developer.mozilla.org/en-US/docs/Web/API/Document/NodeIterator
let nodeWalker = document.createNodeIterator(
/* the element to start iteration from */
root,
/* ignore everything except text nodes */
NodeFilter.SHOW_TEXT,
/* further filter to only grab ones that will get replacements */
{
acceptNode(node) {
if (targetRegex.test(node.nodeValue)) {
return NodeFilter.FILTER_ACCEPT;
} else {
return NodeFilter.REJECT;
}
}
},
);
// I'm going to use a loop here, and this variable holds
// the current node in that loop.
let currentNode;
// I initialise by moving to the first node. While there *is* a node...
while ((currentNode = nodeWalker.nextNode())) {
// Create a replacement string for the text...
let replacement = currentNode.nodeValue.replace(targetText, replacementText);
// ....then set that as the node value:
currentNode.nodeValue = replacement
}
}
So without all the comments:
function htmlTextSwapper(targetText, replacementText, root = document.querySelector("body")) {
let targetRegex = new RegExp(targetText);
let nodeWalker = document.createNodeIterator(
root,
NodeFilter.SHOW_TEXT,
{
acceptNode(node) {
if (targetRegex.test(node.nodeValue)) {
return NodeFilter.FILTER_ACCEPT;
} else {
return NodeFilter.REJECT;
}
}
},
);
let currentNode;
while ((currentNode = nodeWalker.nextNode())) {
let replacement = currentNode.nodeValue.replace(targetText, replacementText);
currentNode.nodeValue = replacement
}
}
Note that this will work if you run it in the console. If you’re doing something for fun, may work, but it’s a bit fragile, and you’re going to want to adjust it/change it to exactly what you want.
Finally, if you want to change numbers to match a locality (eg how they are printed in a specific country [yours?] rather than in the UK/US), then Intl
is absolutely what you need to use, as @lasjorg says.