登录
首页 >  文章 >  前端

可编辑div实现Python代码高亮不乱序方法

时间:2026-02-22 21:07:03 126浏览 收藏

本文深入剖析了在可编辑 div 中实现 Python 代码高亮时,因直接使用 `innerHTML` 替换内容而导致的文本视觉倒序、光标丢失、DOM 结构破坏及 XSS 风险等核心陷阱,揭示问题本质并非字符反转,而是 `innerText` 与 `innerHTML` 双向转换引发的编辑状态崩溃;文章提供了一套轻量、安全、生产可用的手动高亮方案——通过防抖控制触发频率、纯文本转义规避注入风险、增量正则匹配关键词、精确计算并恢复光标位置,全程避免重写整个 DOM 树,并诚恳建议:除非特殊约束,否则应优先采用 CodeMirror 或 Monaco 等成熟编辑器,以真正解决语法高亮背后复杂的选区管理、增量解析与体验一致性难题。

如何在可编辑 div 中正确实现 Python 代码高亮(避免文本反转)

本文详解 writable div 中因 innerHTML 直接替换导致的文本反转问题,揭示 innerText → innerHTML 双向转换引发的 DOM 结构破坏,并提供安全、可维护的高亮实现方案(含防闪烁优化与事件节流示例)。

本文详解 writable div 中因 `innerHTML` 直接替换导致的文本反转问题,揭示 `innerText` → `innerHTML` 直接赋值引发的 DOM 结构破坏,并提供安全、可维护的高亮实现方案(含防闪烁优化与事件节流示例)。

在可编辑

中实现语法高亮时,一个常见但极易被忽视的陷阱是:直接用 innerHTML = text.replace(...) 覆盖整个内容。这正是你遇到 def 显示为 fed 的根本原因——并非字符真的被反转,而是浏览器在解析 HTML 字符串时,错误地将用户输入的原始文本(含光标位置、换行符、空格等)与动态插入的 标签混合后,重新构建 DOM 时丢失了编辑状态,导致光标跳转、文本错位甚至视觉倒序(尤其在 RTL 环境或复杂嵌套下更明显)。

核心问题在于:

  • writableDiv.innerText 只提取纯文本,丢弃所有格式、换行符标准化(如 \r\n → \n),且不保留光标/选区信息;
  • writableDiv.innerHTML = text.replace(...) 强制重写整个 DOM 子树,浏览器会销毁现有节点并重建,光标必然回到开头,用户输入的实时体验彻底崩溃;
  • 正则全局替换 '...' 插入后,若原文本含 <、>、& 等字符,还会引发 XSS 风险或 HTML 解析异常。

✅ 正确做法:不重写整个 innerHTML,而采用增量 DOM 操作 + 选区保持。以下是轻量级、生产可用的修复方案:

<div id="code-editor" spellcheck="false" contenteditable="true" style="font-family: monospace; white-space: pre; line-height: 1.4;"></div>
const editor = document.getElementById("code-editor");
const pythonKeywords = ["def", "class", "if", "else", "for", "while", "import", "from", "return", "print"]; // 示例关键词
const keywordRegex = new RegExp(`\\b(${pythonKeywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})\\b`, 'g');

// 防抖:避免每输入一个字符都触发高亮(性能关键)
let highlightTimeout;
editor.addEventListener('input', () => {
  clearTimeout(highlightTimeout);
  highlightTimeout = setTimeout(() => {
    highlightKeywords(editor);
  }, 150);
});

function highlightKeywords(el) {
  // 保存当前光标位置
  const selection = window.getSelection();
  const range = selection.getRangeAt(0);
  const preCaretRange = range.cloneRange();
  preCaretRange.selectNodeContents(el);
  preCaretRange.setEnd(range.endContainer, range.endOffset);
  const caretOffset = preCaretRange.toString().length;

  // 安全地将纯文本转为带高亮的 HTML(仅转义必要字符)
  const rawText = el.textContent;
  const escapedText = rawText
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');

  // 使用正则匹配并包裹关键词(注意:仅对纯文本操作,不破坏结构)
  const highlightedHTML = escapedText.replace(keywordRegex, '<span style="color:#f200ff;font-weight:bold;">$1</span>');

  // 关键:用 innerHTML 替换前,先记录光标位置;替换后恢复
  el.innerHTML = highlightedHTML;

  // 恢复光标到原位置(基于字符偏移量)
  restoreCaret(el, caretOffset);
}

function restoreCaret(el, offset) {
  const treeWalker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);
  let node;
  let charCount = 0;

  while (treeWalker.nextNode()) {
    node = treeWalker.currentNode;
    const nodeLength = node.textContent.length;
    if (charCount + nodeLength >= offset) {
      const range = document.createRange();
      const posInNode = offset - charCount;
      range.setStart(node, posInNode);
      range.setEnd(node, posInNode);
      const selection = window.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);
      return;
    }
    charCount += nodeLength;
  }
}

⚠️ 重要注意事项:

  • 永远不要在 contenteditable 中直接 innerHTML = ...:它会重置 DOM 树,破坏光标、撤销栈、IME 输入状态;
  • 关键词正则需转义特殊字符:pythonKeywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) 防止正则语法错误;
  • 必须做 HTML 实体转义:否则用户输入
资料下载
最新阅读
更多>
课程推荐
更多>
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    立即学习 543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    立即学习 516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    立即学习 500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    立即学习 487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    立即学习 485次学习