news 2026/10/3 2:30:59

30-seconds-of-code:用 JavaScript 从字符串创建 HTML 元素(createElement 技巧)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
30-seconds-of-code:用 JavaScript 从字符串创建 HTML 元素(createElement 技巧)
  • 教程
  • 文档

【免费下载链接】30-seconds-of-code

Coding articles to level up your development skills

项目地址:https://gitcode.com/gh_mirrors/30/30-seconds-of-code
点击查看免费下载

导读

在浏览器端开发中,DOM 操作是 JavaScript 诞生的核心动机之一。很多时候,我们并不想立即把新元素插入文档,而是希望先从一段 HTML 字符串"解析"出一个可用的元素对象,待条件满足后再挂载到页面。本文基于 30-seconds-of-code 仓库中的 create-element 片段,完整讲解如何用Document.createElement()、Element.innerHTML与Element.firstElementChild三行核心逻辑实现"字符串 → 元素"的转换,并延伸对比仓库内创建元素的其它思路(编程式创建、渲染整棵 DOM 树、按位置插入 HTML 字符串),让你在实战中按需选择最合适的方案。

核心需求:在不挂载文档的前提下,从字符串得到元素

页面开发中常见的场景是:接口返回了一段 HTML 模板字符串,或者你在内存中拼装了一段标记,需要先得到一个真正的元素对象做进一步处理(读取属性、修改内容、绑定事件),最后再决定是否以及如何插入文档。

此时如果直接把它丢进document.body.innerHTML,会立刻触发重排与渲染;而如果只是为了拿到元素本身,更干净的做法是在内存中完成解析。这正是 30-seconds-of-code 中createElement片段的定位——它位于 DOM 操作合集(js/dom-manipulation)之中,与"编程式创建元素"、"渲染 DOM 树"、"按位置插入 HTML 字符串"等片段互为补充。

三行核心逻辑:createElement 的完整实现

整个片段的实现非常紧凑,全部逻辑建立在三个浏览器原生 API 上:

const createElement = str => { const el = document.createElement('div'); el.innerHTML = str; return el.firstElementChild; };

各步骤逐一拆解

  1. Document.createElement('div'):在内存中创建一个全新的div元素。注意此时该元素尚未与文档树产生任何连接,不会触发任何渲染。
  2. el.innerHTML = str:把传入的 HTML 字符串交给浏览器解析器,作为div的内部结构写入。这是把字符串变成真实 DOM 节点的关键一步,浏览器会按标准 HTML 解析规则生成对应的子节点。
  3. el.firstElementChild:div本身只是一个临时"容器",我们真正想要的是字符串对应的那个元素,因此取出它的第一个元素子节点并返回。外层div随即被丢弃,不会污染文档。

实战验证

const el = createElement( `<div class="container"> <p>Hello!</p> </div>` ); console.log(el.className); // 'container' const other = createElement( `<p>Hi!</p> <div>Bye!</div>` ); console.log(other.tagName); // 'P' (only the first element is returned)

从第一个例子可以看到,返回的元素完整保留了字符串中的class属性;第二个例子则演示了片段中特别强调的边界行为——当字符串包含多个顶层元素时,只有第一个元素会被返回。

关键边界:多个顶层元素时只返回第一个

原片段在代码注释与正文中都给出了明确警告:

如果字符串包含多个元素,只有第一个会被返回。

这是firstElementChild的天然语义:它始终返回容器下的第一个元素子节点,字符串中后续的同级元素会被"遗弃"在临时的外层div里,而该div从未挂载,随后被垃圾回收。

因此,这个工具函数最适用于单一根元素的字符串。如果你的字符串是多个平级片段(如一组li),请优先考虑Element.insertAdjacentHTML()这类按位置插入的方案(见下文),而不是试图让createElement返回"全部元素"。

配套思路一:编程式创建元素(不依赖字符串)

与"字符串解析"形成对照的是仓库中 create-html-elements 片段 的编程式方案。它直接使用Document.createElement()创建元素,并通过Object.entries()遍历属性对象来批量赋值,还支持用prepend/append控制插入位置:

const createElement = (el, parent, prepend = false) => { const { nodeName = 'div', ...attrs } = el; const element = document.createElement(nodeName); Object.entries(attrs).forEach(([attr, value]) => { element[attr] = value; }); if (prepend) parent.prepend(element); else parent.append(element); };

两者的适用边界非常清晰:

  • 字符串方式(本文主题):适合已有 HTML 模板字符串、只想快速拿到元素对象的场景,代码最简洁;
  • 编程式方式:适合需要批量生成多个同构元素、避免大量重复属性设置代码的场景,且能直接用对象表达属性与文本内容。

在 DOM 操作合集 中,这两个片段被编排在一起,正是因为它们解决的是同一问题的两条互补路径。

配套思路二:渲染整棵 DOM 树(递归方案)

如果字符串只是单个元素,本文方案足够;但当你需要渲染一整棵 DOM 树时,仓库的 render-dom-element 片段 给出了一个更接近 React 虚拟 DOM 思路的递归实现。它以{ type, props }对象描述元素,用Document.createTextNode()处理文本节点、Document.createElement()创建元素节点,并通过addEventListener挂载on前缀的事件监听器后递归渲染子节点。

原片段也明确指出:该实现仅用于演示理解,缺少 React/Preact 的诸多特性与优化。因此,判断依据很简单——单元素字符串用本文的createElement一行即可,复杂渲染需求则应考虑完整框架或更健壮的渲染库。

配套思路三:按位置插入 HTML 字符串(insertAdjacentHTML)

当"解析后手动挂载"显得多余,而你明确知道元素要插到哪个位置时,仓库的 insert-html-string-before-or-after-element 片段 提供了更直接的 API:

const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString); const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);

Element.insertAdjacentHTML()支持'beforebegin'、'afterbegin'、'beforeend'、'afterend'四个位置,且不要求字符串具有单一根元素,可以一次性插入多个平级节点。它适合"目标位置明确、直接落盘"的场景;而本文的createElement适合"先在内存中取得元素、后续按需处理"的场景——两者结合,可以覆盖从解析到插入的完整链路。

使用注意事项

  • 字符串必须是合法的 HTML 片段:innerHTML交由浏览器解析器处理,语法错误或未闭合标签会按容错规则被修正,不会抛出异常,请对来源内容保持预期管理;
  • 不要用innerHTML拼接不可信输入:当字符串来自用户输入等不可信来源时,直接赋值存在注入风险,应优先采用文本节点赋值或转义处理;这也正是前文"编程式创建"方案在安全性上的优势;
  • 返回的是引用而非副本:firstElementChild返回的节点与临时div内部结构共享同一引用,后续对返回元素的操作会直接作用于该节点;
  • 不触发渲染:整个解析过程发生在内存中,未挂载前不会引起页面重排或重绘,这也是该技巧适合"先构建、后挂载"流程的原因。

小结

30-seconds-of-code 的createElement片段用三行代码解决了"从字符串创建元素且不挂载文档"这一高频需求,核心是Document.createElement()+Element.innerHTML+Element.firstElementChild的组合,并明确了"多顶层元素只取第一个"的边界。配合仓库中编程式创建(create-html-elements)、递归渲染 DOM 树(render-dom-element)与按位置插入(insert-html-string-before-or-after-element)三个片段,你可以在"字符串解析、编程式构建、整树渲染、定点插入"之间按场景自由切换,写出更简洁、更可控的 DOM 操作代码。

  • 教程
  • 文档

【免费下载链接】30-seconds-of-code

Coding articles to level up your development skills

项目地址:https://gitcode.com/gh_mirrors/30/30-seconds-of-code
点击查看免费下载
上一篇:OmO Senpi 内存组件 worker 模块解析:Reflection/Dream 子进程执行与持久化运行生命周期
下一篇:Vitess 18.0.0 升级实战指南:外键模式、新命令行参数与破坏性变更全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 2:30:56

Go 内存对齐与数据结构性能:字段顺序影响 50% 内存占用

Go 内存对齐与数据结构性能&#xff1a;字段顺序影响 50% 内存占用struct 字段顺序变了&#xff0c;内存占用也变了。写 Go 服务必须懂内存对齐规则&#xff0c;能让数据结构更紧凑。一、对齐基础 CPU 读内存一般按 8 字节对齐。如果 struct 内部字段顺序错乱&#xff0c;编译时…

作者头像 李华
网站建设 2026/10/3 2:30:53

蓝牙芯片驱动开发-第6章第5题-如何确保ACL数据包的正确识别与处理

蓝牙面试题解析:如何确保 ACL 数据包的正确识别与处理? 难度:⭐⭐⭐⭐ 较难 | 场景:社招二面/三面、蓝牙驱动开发 | 高频:🔥🔥🔥🔥 标准答案 ACL 数据包的识别与处理通过 类型标识 + 句柄匹配 + 完整性校验 + 缓冲管理 四层保障: ① 包类型识别(H4 传输层) …

作者头像 李华
网站建设 2026/10/3 2:30:23

京东云老用户主机续费省钱攻略:优惠活动全拆解

云主机续费这件事&#xff0c;一聊起来基本全是眼泪。新用户首年三折五折拿主机&#xff0c;看着挺香&#xff0c;可一到第二年续费&#xff0c;价格“duang”一下回到原价&#xff0c;账单直接翻倍甚至翻两倍。很多人就是被这一刀“劝退”的&#xff0c;干脆换一家重新“上车”…

作者头像 李华