一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何安全地将HTML元素结构转换为带语法高亮的可读代码块

时间:2026-06-06 10:27:53 编辑:袖梨 来源:一聚教程网

本文介绍一种基于 DOM 解析而非正则表达式的可靠方法,将真实 HTML 元素自动转换为结构化、可着色的 <span> 包裹代码字符串,避免转义错误与 XSS 风险,同时支持精准语法高亮(如标签名、属性、引号、文本内容等)。

本文介绍一种基于 dom 解析而非正则表达式的可靠方法,将真实 html 元素自动转换为结构化、可着色的 `` 包裹代码字符串,避免转义错误与 xss 风险,同时支持精准语法高亮(如标签名、属性、引号、文本内容等)。

在前端开发中,常需在文档或演示页中“展示 HTML 代码本身”——例如教学示例、组件文档或调试面板。若直接使用 innerHTML + 正则替换(如 replace(/</g, '<... xss>绕过字符串解析,直接利用浏览器已解析的 DOM 树。

以下是一个生产就绪的 generateHTMLBlock 函数实现,它接收一个 DOM 节点,递归遍历其结构,为每类节点(元素、文本、注释)生成语义化 <span> 标签,并赋予对应 CSS 类名用于着色:

function generateHTMLBlock(node) {  const parent = document.getElementById('output'); // 目标容器  // 辅助函数:批量创建带 class 和 textContent 的 span  function ce(parent, ...args) {    for (let i = 0; i < args.length; i += 2) {      const e = document.createElement('span');      e.className = args[i];      e.textContent = args[i + 1];      parent.appendChild(e);    }  }  // 递归生成节点表示  function genNode(parent, node) {    const wrapper = document.createElement('div');    parent.appendChild(wrapper);    wrapper.className = 'node';    switch (node.nodeType) {      case Node.ELEMENT_NODE:        wrapper.classList.add('element');        const tagName = node.nodeName.toLowerCase();        // 开始标签: <button        ce(wrapper, 'tagStart', '<', 'tagName', tagName);        // 属性: class="btn btn-primary"        for (const attrName of node.getAttributeNames()) {          const attrValue = node.getAttribute(attrName).replace(/"/g, '"');          ce(wrapper,            'space', ' ',            'attrName', attrName,            'attrEqual', '=',            'attrQuote', '"',            'attrValue', attrValue,            'attrQuote', '"'          );        }        ce(wrapper, 'tagEnd', '>');        // 子节点(文本、子元素、注释)        for (const child of node.childNodes) {          genNode(wrapper, child);        }        // 结束标签: </button>        ce(wrapper, 'tagStart', '</', 'tagName', tagName, 'tagEnd', '>');        break;      case Node.TEXT_NODE:        wrapper.classList.add('text');        const trimmedText = node.textContent.trim();        if (trimmedText) {          ce(wrapper, 'textValue', trimmedText);        }        break;      case Node.COMMENT_NODE:        wrapper.classList.add('comment');        ce(wrapper, 'commentStart', '<!--', 'commentValue', node.textContent, 'commentEnd', '-->');        break;    }  }  genNode(parent, node);}

调用方式简洁明了:

<!-- 示例按钮 --><div>  <button id="btn" class="btn btn-primary" data-value="btn-primary" type="button">Button</button></div><!-- 输出容器 --><pre><code id="output"></code></pre><script>  // 传入真实 DOM 节点(非字符串!)  generateHTMLBlock(document.getElementById('btn'));</script>

关键优势

立即学习“前端免费学习笔记(深入)”;

  • 零正则风险:不依赖 replace() 处理 HTML 字符,完全规避标签误匹配、属性值截断等问题;
  • 自动转义安全:通过 textContent 设置内容,天然防御 XSS(即使 data-value 含 <script> 也会被原样显示为文本);
  • 结构清晰可扩展:每个语法单元(<, button, class=, "...", >)独立成 <span>,CSS 可精细控制颜色、间距、字体;
  • 支持复杂节点:完美处理换行、多空格、注释、嵌套元素(如 <button>Text<span>inner</span></button>)。

? 配套样式建议(确保可读性):

#output {   background: #1d1b1b;   color: #aaa;   padding: 1rem;   overflow-x: auto;}.node { white-space: pre-wrap; font-family: 'SFMono-Regular', Consolas, monospace; }.node > .node { margin-left: 2ch; }.tagStart, .tagEnd { color: #fff; }.tagName { color: #f09449; }         /* 标签名:button */.attrName { color: #88afce; }       /* 属性名:class, data-value */.attrEqual, .attrQuote { color: #fff; }.attrValue { color: #bfbd68; }      /* 属性值:btn btn-primary */.textValue { color: #fff; }        /* 文本内容 */.comment { color: #999; }

⚠️ 注意事项

  • 不要对 innerHTML 字符串调用此函数——必须传入已挂载的 DOM 节点(如 document.querySelector('button'));
  • 若需高亮 < > 符号本身(非标签),应额外增加 .chev 类并统一着色(如示例中蓝色),但注意区分 tagStart/tagEnd 与纯符号;
  • 对于大型 DOM 树,建议添加节流或异步渲染逻辑,避免阻塞主线程。

该方法将“展示代码”从脆弱的字符串操作升级为健壮的 DOM 遍历,兼顾安全性、可维护性与专业表现力,是构建高质量前端文档与交互式教程的理想实践。

热门栏目