replace-dom-text

A modern TypeScript + ESM rewrite of James Padolsey’s findAndReplaceDOMText
Search, replace, or wrap matching text in the DOM — even when matches span multiple nodes.
Now written in TypeScript, distributed as ES modules, and fully typed for better developer experience.
✨ Features
- 🔍 Find text using RegExp or string
- 🧩 Replace or wrap matches with text or DOM elements
- 💬 Supports cross-node matches
- 🧠 Fully typed API with IntelliSense
- ⚙️ Built-in
preset: 'prose' for natural text replacement
- ♻️ Supports revert() to undo replacements
- 📦 Ships as pure ESM +
.d.ts definitions
🚀 Installation
npm install replace-dom-text
Or via CDN:
<script type="module">
import replaceDOMText from 'https://cdn.jsdelivr.net/npm/replace-dom-text/+esm'
</script>
🧩 Basic Usage
<p id="t">123 456 Hello</p>
import replaceDOMText from 'replace-dom-text'
replaceDOMText(document.getElementById('t')!, {
find: /Hello/,
wrap: 'em',
})
Result:
<p id="t">123 456 <em>Hello</em></p>
💡 Cross-Node Matching
<p id="t">123 456 Hell<span>o Goodbye</span></p>
replaceDOMText(document.getElementById('t')!, {
find: /Hello/,
wrap: 'em',
})
Result:
<p id="t">
123 456 <em>Hell</em><span><em>o</em> Goodbye</span>
</p>
⚙️ API
replaceDOMText(
element: Element | Text,
options: Options,
): Finder
Options
| find | RegExp | string | Text or pattern to search for. Add /g for multiple matches. |
| replace | string | (portion, match) => string | Node | Replace matches with text or a Node. Supports $0, $1, $&, $', `$``. |
| wrap | string | Node | Wrap each match in an element ('span', 'em', etc.) or clone a provided node. |
| wrapClass | string | CSS class to apply to the wrapping element. Ignored if wrap is not used. |
| portionMode | 'retain' | 'first' | Preserve node boundaries (retain, default) or merge into the first node (first). |
| filterElements | (el: Element) => boolean | Return false to skip specific elements. |
| forceContext | boolean | (el: Element) => boolean | Force elements to act as their own matching contexts. |
| preset | 'prose' | Ignore non-text elements (<script>, <svg>, etc.) and restrict matches within block elements. |
🔤 Using a Function for replace
replaceDOMText(document.getElementById('container')!, {
find: 'function',
replace: (portion, match) => `[${portion.index}]`,
})
Input:
<div id="container">Explaining how to write a replace <em>fun</em>ction</div>
Output:
<div id="container">Explaining how to write a replace <em>[0]</em>[1]</div>
🎨 Wrapping Matches
replaceDOMText(document.getElementById('container')!, {
find: 'with ',
wrap: 'em',
wrapClass: 'highlight',
})
CSS:
.highlight {
background: yellow;
}
Result:
<em class="highlight">with </em>
🧱 Context Control
Prevent matches from crossing certain element boundaries:
replaceDOMText(document.getElementById('test')!, {
find: 'amazing',
wrap: 'em',
forceContext: el => el.matches('p'),
})
🧰 Instance API
const finder = replaceDOMText(node, options)
finder.revert()
⚠️ Reversion only works if the DOM has not been modified after replacement.
🧠 Presets
preset: 'prose'
Optimized for prose and text content.
Skips non-prose elements and prevents matches across block boundaries.
replaceDOMText(element, {
preset: 'prose',
find: 'something',
replace: 'something else',
})
🧾 Type Definitions
TypeScript users get full type support out of the box:
import replaceDOMText, {
Finder,
Portion,
Options,
} from 'replace-dom-text'
🧩 Example Projects
📜 License
MIT License
Originally by James Padolsey
TypeScript & ESM rewrite © 2025 by Yufan Sheng