盘点浏览器翻译插件对DOM结构的影响及解决

翻译插件能够帮助我们快捷完成网页的翻译,但这种便利是有代价的,因为它会干扰许多现代网站的运作。这是因为翻译插件以破坏DOM结构的方式操作 DOM ,这会带来如 removeChild 方法的错误、让网站崩溃

本文将会探讨以下几点:

  • 翻译实际带来的问题 - 最近遇到的bug

  • 翻译插件的工作原理

  • 可能的解决方案

遇到问题

这段时间,有个别用户反馈在使用某项目时出现网站崩溃的情况:在选择框中输入文本后页面白屏

收到反馈后第一反应肯定是想办法复现它,可是在本地怎么就是复现不出来,即使使用同样的账号、网址和操作

--> 这个项目已经上线很长时间,大部分用户在使用上也没有出现类似问题,难道只能跑去找用户、用他电脑现场排查下吗?

在准备约用户前,突然灵光一动,想到之前有遇到过翻译导致的问题,也知道翻译会改变dom结构,于是赶紧打开翻译功能试了下,果然,问题复现了!

最小复现案例和步骤:

  • 可多选的选择器

复制 antd 官网中的 treeSelect 示例代码,生成一个支持多选的选择器

  • 输入内容
  • 删除内容

删除后出现报错:

原因探究

翻译插件运行时到底做了什么

既然已经知道问题是由翻译引起,那就得知道在翻译时,到底在页面上做了什么手脚

对于一段简单的 HTML 元素:

css 复制代码
<div>hello</div>

在开启翻译后,会变为

css 复制代码
<div><font>你好</font></div>

它会对页面上的文本内容进行翻译,并将翻译结果使用 font 标签进行包裹

看似只是改变了文本,并给文本加了个新标签,但实际的dom结构变化如下:

从图里可以看出,原始的TextNode文本实际已经被卸载了! 这个对 DOM 的影响就是产生问题的主要原因。

什么时候会发生问题

对节点的操作包括了更新值、删除或者添加子项,翻译插件对 DOM 结构的影响会对节点的这三个操作都带来影响

更新节点 -> 未及时更新文本 /数字

最小复现示例如下,当点击按钮 addCount 后,页面上对应的count没有同步更新

javascript 复制代码
import { useEffect, useState } from 'react';

function App() {
  const [count, setCount] = useState(1);
  
  useEffect(() => {
    console.log(count);
  }, [count]);
  
  return (
    <div>
      hello World{count}
      <button onClick={() => setCount((count) => count + 1)}>addCount</button>
    </div>
  );
}
export default App;

原因在于翻译插件将hello World{count}的内容合并到了一个<font>标签中,导致真实的{count}对应的节点已经不存在页面中了,故更新无效;

这问题不会导致浏览器崩溃,甚至不会有报错信息,在排查起来实际会更加困难,同时对用户也更有误导性

删除节点 -> 页面崩溃

最小复现示例如下,当点击按钮 addCount 后,页面报错

javascript 复制代码
import { useState } from 'react';

function App() {
  const [count, setCount] = useState(1);
  return (
    <div>
      hello World
      {count % 2 && <span>此时是{count}</span>}
      <button onClick={() => setCount((count) => count + 1)}>addCount</button>
    </div>
  );
}
export default App;

原因在于当count从1变为2后,React 尝试通过<span>的父级来卸载文本内容,却无法找到对应的节点

报错信息如下:

csharp 复制代码
Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

复现要求:子节点的 TextNode 个数大于1,且数量会发生改变

复现场景1:有条件渲染的节点具有同级的兄弟节点

less 复制代码
// 1.会出问题
  <div>
      hello World
      {count % 2 && <span>此时是{count}</span>}
  </div>

// 2. 会出问题
  <div>
      {count % 2 && <span>此时是{count}</span>}
       hello World
  </div>


// 3. 没有问题
    <div>{count % 2 && <span>此时是{count}</span>}</div>

复现场景2:使用三元表达式渲染不同数量的文本节点

xml 复制代码
 <div>
        {count % 2 ? (
          <span>此时是{count}</span>
        ) : (
          <>
            <span>此时是</span>
            {count}
          </>
        )}
      </div>

除了上述两种场景,还有很多方式可以改变渲染的 TextNode 数量,这使得很难找到解决所有情况的有效方法

添加节点 -> 页面崩溃

最小复现示例如下,当count从3变为4时,控制台报错

javascript 复制代码
import { useState } from 'react';

function App() {
  const [count, setCount] = useState(1);
  return (
    <div>
      hello World
      <div>
        {count % 2 ? <span>此时是{count}</span> : count}
        {count > 2 && 'Oops快出问题了'}
      </div>
      <button onClick={() => setCount((count) => count + 1)}>addCount</button>
    </div>
  );
}
export default App;

报错信息:

csharp 复制代码
Uncaught NotFoundError: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

解决方案

目前还没有终极解决方案,禁用翻译暂时是最好的解决办法,在需要国际化时,通过自己的应用程序实现本地化,使机器翻译变得没有必要。

通过设置HTML标签的lang 换为 zh,浏览器即使开启了自动翻译功能,也不会再自动翻译我们的页面了

html 复制代码
<html lang="zh">
...
</html>

不过这并不能阻止用户手动强行翻译,因此如果要从根本上禁止翻译,可以结合translate="no"一起使用

html 复制代码
<html lang="zh" translate="no">
...
</html>

不仅仅是翻译插件

翻译插件导致问题的根本原因是对DOM结构的干扰,这可能会对JavaScript操作DOM节点造成影响,这类问题在采用"虚拟DOM"技术的框架中更为明显,因为虚拟DOM通过保留对所有DOM节点的引用,实现更新时只需要更改实际发生变化的DOM部分(这一过程称为协调),因此它要求对DOM具有完全的控制和独占性,这意味着在与第三方扩展插件协作时,若这些插件修改了页面的DOM结构,就可能引发上述提到的问题

查找 BUG 经验和思路小结

  • 当所有用户都出问题时,那一定是代码有问题;

  • 当极小部分用户出问题时,考虑是否是翻译插件导致;

  • 当只有一些高级用户(会使用浏览器插件的人)出问题时,考虑是否为第三方拓展插件导致

参考文章:

  1. 关于 Google 翻译导致 React(和其他 Web 应用程序)崩溃的一切
  2. github-issue - 使 React 对来自 Google 翻译 的 DOM 突变具有弹性
  3. stackoverflow --- React DOMException:在 'Node' 上执行 'removeChild' 失败:要删除的节点不是该节点的子节点
  4. chrome浏览器翻译可能引发的页面问题
相关推荐
passerby606123 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了30 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅33 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc