React如何移除HTML字符串中指定元素并完成内容规范化
React中预处理HTML字符串移除指定标签、清理属性的实现方案
不要用正则硬匹配HTML字符串做处理,这类方案边界问题极多——比如标签属性中包含>字符、HTML注释中出现目标标签名、标签大小写不统一等场景都会导致匹配失效,最稳妥的实现是借助浏览器原生DOMParser把字符串解析为DOM树,完成操作后再序列化为字符串使用。
实现步骤
- 用
DOMParser把原始HTML字符串解析为独立的DOM文档,整个解析过程在内存中完成,不会污染当前页面的DOM结构 - 收集所有需要移除的标签节点(比如
img、svg、script、iframe等),统一从DOM树中删除 - 遍历所有保留的节点,按照属性白名单清理冗余属性,不在白名单内的属性全部移除
- 把处理完成的DOM内容序列化为HTML字符串,再传入
dangerouslySetInnerHTML渲染
可直接复用的代码
/** * HTML字符串清洗工具 * @param {string} rawHtml 原始待处理HTML字符串 * @param {string[]} removeTags 需要彻底移除的标签名列表,默认移除常见风险标签 * @param {string[]} allowedAttrs 允许保留的属性白名单,不在列表内的属性全量清理 * @returns {string} 处理后的HTML字符串 */ function sanitizeHtml( rawHtml, removeTags = ['img', 'svg', 'script', 'iframe', 'embed'], allowedAttrs = ['class', 'id'] ) { // 解析为独立DOM文档,外层套包裹节点方便统一处理 const parser = new DOMParser(); const doc = parser.parseFromString(`<div id="__sanitize_root">${rawHtml}</div>`, 'text/html'); const rootContainer = doc.getElementById('__sanitize_root'); // 移除所有指定标签 removeTags.forEach(tagName => { const targetNodes = rootContainer.getElementsByTagName(tagName); // getElementsByTagName返回动态集合,转成静态数组后遍历删除,避免漏删 Array.from(targetNodes).forEach(node => node.remove()); }); // 清理冗余属性 const allRemainNodes = rootContainer.getElementsByTagName('*'); Array.from(allRemainNodes).forEach(node => { const nodeAttrs = Array.from(node.attributes); nodeAttrs.forEach(attr => { if (!allowedAttrs.includes(attr.name)) { node.removeAttribute(attr.name); } }); }); return rootContainer.innerHTML; } // 组件内使用示例 function ContentBlock() { const originHtml = '<div class="content" data-track="123"><img src="ad.png" /><p id="desc" onclick="alert(1)">正文内容</p><svg class="icon"><path/></svg></div>'; // 按需求配置:移除img、svg标签,仅保留class、id属性 const cleanedHtml = sanitizeHtml(originHtml, ['img', 'svg'], ['class', 'id']); return ( <div dangerouslySetInnerHTML={{ __html: cleanedHtml }} /> ) }
注意事项
- 不要依赖正则做HTML清洗:正则无法覆盖HTML语法的所有边界情况,自己写的正则匹配规则很容易出现漏删、误删正常内容的问题
- 属性过滤优先用白名单机制,不要用黑名单:比如想过滤事件属性如果只删
onclick、onerror很容易漏过其他事件绑定,直接只保留业务明确需要的属性,安全隐患会小很多 - 如果需要更细粒度的规则(比如仅允许
a标签保留href属性、其他标签不允许),只需要在属性清理逻辑里加一层标签名判断即可,扩展成本很低 - 即使做了上述处理,
dangerouslySetInnerHTML依然存在XSS风险,如果原始HTML来自不可信的用户输入,建议根据业务场景补充更严谨的过滤规则,避免注入风险
内容的提问来源于stack exchange,提问作者Mahmoud Mousa Hamad
相关产品推荐
相关产品推荐

