如何用Violentmonkey用户脚本为网页货币数字添加美元符号
网页数值自动添加$符号的用户脚本修复方案
问题概述
需要给网页中无货币符号的数值(如25.00格式化为$25.00、3,100.00格式化为$3,100.00)添加$符号,使用Violentmonkey扩展实现,但现有脚本未生效,调整正则表达式后仍无效果。
原用户脚本代码
(function() { 'use strict'; function checkReadyState() { if (document.readyState === 'complete' || document.readyState === 'interactive') { // Regex to find numbers with two decimal places (with or without commas) const numberRegex = /\d+(,\d+)*\.\d\d/; // Find all text nodes (more general than specific elements) const textNodes = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); let currentNode; // Function from StackOverflow const money = (amount, currency = 'USD', locale = 'en-US') => { return Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount); } while (currentNode = textNodes.nextNode()) { const text = currentNode.nodeValue; const modifiedText = text.replace(numberRegex, (match) => { // Don't add '$' if it already exists if (match.startsWith('$')) { return match; } else { const amount = parseFloat(match.replace(/,/g, '')); // Remove commas return money(amount); } }); if (modifiedText !== text) { const newNode = document.createTextNode(modifiedText); currentNode.parentNode.replaceChild(newNode, currentNode); } } } else { // Wait for the document to be ready document.addEventListener('DOMContentLoaded', checkReadyState); } } // Call the function initially checkReadyState(); })();
网页示例代码
<p>25.00</p> <p>3,100.00</p> <div data-cellname="[object Object]" class="css-12hdbj5 css-nil">3,105.00</div> <br> <span data-testid="__global!balance-1a1d49c3-9cb8-4c67-896c-0122ac627f08" data-cellname="__global!balance-1a1d49c3-9cb8-4c67-896c-0122ac627f08" class=" css-1ai95jy">45.14</span> <br> <span class=" css-bs1bqe">90.00</span> <br>
问题原因分析
- 正则表达式缺陷:原正则缺少全局匹配标志(
g),仅替换每个文本节点的第一个匹配项;且无单词边界(\b),可能误匹配长文本中的片段。 - DOM遍历问题:使用
TreeWalker时,替换文本节点会打乱遍历顺序,导致部分节点被跳过。 - 未处理动态内容:脚本仅在初始DOM加载完成时运行一次,无法处理网页后续异步加载的数值。
修复后的脚本代码
(function() { 'use strict'; // 格式化数值为带$的货币格式 const formatCurrency = (amountStr) => { // 移除千分位逗号,转换为数字 const amount = parseFloat(amountStr.replace(/,/g, '')); if (isNaN(amount)) return amountStr; return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); }; // 遍历并处理文本节点 function processTextNodes(node) { if (node.nodeType === Node.TEXT_NODE) { const originalText = node.nodeValue; // 匹配独立的、带两位小数的数值(支持千分位),全局替换 const modifiedText = originalText.replace(/\b\d{1,3}(,\d{3})*\.\d{2}\b/g, (match) => { // 跳过已带$的数值 if (match.startsWith('$')) return match; return formatCurrency(match); }); if (modifiedText !== originalText) { const newTextNode = document.createTextNode(modifiedText); node.parentNode.replaceChild(newTextNode, node); } } else if (node.nodeType === Node.ELEMENT_NODE && !['SCRIPT', 'STYLE'].includes(node.tagName)) { // 递归处理子节点,跳过脚本和样式标签 Array.from(node.childNodes).forEach(child => processTextNodes(child)); } } // 处理当前DOM内容 function processPage() { processTextNodes(document.body); } // 监听DOM变化,处理动态加载内容 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { processTextNodes(node); } }); }); }); // 初始化执行 function init() { processPage(); // 开始监听DOM变化 observer.observe(document.body, { childList: true, subtree: true }); } // 确保DOM加载完成后执行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();
关键改动说明
- 正则优化:使用
/\b\d{1,3}(,\d{3})*\.\d{2}\b/g,\b确保匹配独立数值,\d{1,3}(,\d{3})*准确匹配千分位格式,g实现全局替换。 - 遍历方式调整:改用递归遍历文本节点,避免
TreeWalker的遍历中断问题,同时跳过SCRIPT和STYLE标签防止误修改。 - 动态内容处理:添加
MutationObserver监听DOM变化,自动处理后续加载的数值内容。 - 格式化函数优化:增加数值有效性判断,避免转换失败导致的异常。
内容的提问来源于stack exchange,提问作者Michael Gunter
相关产品推荐
相关产品推荐

