HTML 元素转 JavaScript 对象:DOM 属性、dataset 与 HTML 字符串解析
把 HTML 元素变成 JS 可操作的“对象”,靠的是 DOM(文档对象模型)——浏览器把标签解析成节点树,每个节点都是一个对象,有属性、方法与事件。这跟“把 HTML 字符串转成 JSON 对象”是两件完全不同的事,很多新手会混。本文把两套做法都写清楚。

一、DOM 方向:把页面元素当对象用
元素对象自带的信息非常丰富:
const el = document.querySelector('#card');
const info = {
tag: el.tagName, // ‘DIV’
id: el.id,
classes: […el.classList], // [‘card’,‘active’]
attrs: Object.fromEntries(
[…el.attributes].map(a => [a.name, a.value])
),
dataset: { …el.dataset }, // data-* 属性集合
rect: { // 尺寸与位置
w: el.offsetWidth, h: el.offsetHeight,
x: el.offsetLeft, y: el.offsetTop
},
styles: getComputedStyle(el), // 计算后样式(只读为主)
text: el.textContent.trim(),
html: el.innerHTML,
childrenCount: el.children.length
};
console.log(info);
几个要点:dataset 是 data-xxx 属性的驼峰映射(data-user-id → dataset.userId),做组件配置最常用;getComputedStyle 拿到的是渲染后的值,比 el.style 更真实;offsetLeft 相对定位父级,要页面绝对坐标用 getBoundingClientRect()。
二、把整棵树转成普通对象(自定义遍历)
浏览器不会把整页给你一个纯数据对象,需要自己走:
function toNode(el) {
return {
tag: el.tagName.toLowerCase(),
attrs: Object.fromEntries([...el.attributes].map(a => [a.name, a.value])),
text: el.childElementCount ? '' : el.textContent.trim(),
children: [...el.children].map(toNode)
};
}
const tree = toNode(document.getElementById('app'));
JSON.stringify(tree, null, 2);
这适合做页面结构快照、模板调试、简易渲染器。注意它只走 element 节点,忽略文本与注释节点;要完整表达需改用 NodeFilter 遍历 childNodes。
三、字符串方向:HTML 文本 → 对象/JSON
如果拿到的是后端返回的 HTML 字符串,先解析成节点再取数据:
const html = `
<ul id="menu">
<li data-id="1" class="on">首页</li>
<li data-id="2">文档</li>
</ul>`;
// 方式1:DOMParser(推荐,不会执行脚本)
const doc = new DOMParser().parseFromString(html, ‘text/html’);
const items = […doc.querySelectorAll(‘#menu li’)]
.map(li => ({ id: +li.dataset.id, label: li.textContent, on: li.classList.contains(‘on’) }));
// 方式2:template 元素(同样安全)
const tpl = document.createElement(‘template’);
tpl.innerHTML = html;
const first = tpl.content.querySelector(‘li’);
安全红线:不要用 div.innerHTML = html 解析不可信内容再插入页面。HTML 里的 <img onerror>、<script> 会造成 XSS;DOMParser 与 <template> 生成的节点不在文档中,脚本不会执行,是相对安全的解析方式。需要展示富文本时,还要过一遍净化库(如 DOMPurify)。
四、属性/参数转对象的三个常用小工具
// 1. URL 查询串 → 对象
const query = Object.fromEntries(new URLSearchParams(location.search));
// 2. 表单 → 对象
const form = Object.fromEntries(new FormData(formEl).entries());
// 3. data-* → 对象(含 JSON 值)
// <div data-config=‘{“limit”:10}’>
const cfg = JSON.parse(el.dataset.config);
FormData + Object.fromEntries 是收集表单最省事的方式,多选框与文件字段要注意取值差异(同名多值用 getAll())。
五、反向:对象 → HTML
function render(data) {
const li = document.createElement('li');
li.dataset.id = data.id;
li.textContent = data.label; // 用 textContent,天然防 XSS
li.className = data.on ? 'on' : '';
return li;
}
const ul = document.getElementById('menu');
ul.replaceChildren(...list.map(render)); // 一次性替换,性能优于循环 append
能不用 innerHTML += 就不用:每次 += 都会重新解析并销毁已有节点,既慢又可能把用户输入当 HTML 执行。
六、常见问题 FAQ
问:el.style.width 读到空字符串?
答:它只反映行内样式,样式表里的值要用 getComputedStyle(el).width。
问:innerHTML 与 outerHTML 区别?
答:innerHTML 是元素内部内容,outerHTML 包含元素自身标签;给 outerHTML 赋值会替换整个节点,之前的引用失效。
问:能不能直接把 DOM 对象 JSON.stringify?
答:不能,DOM 节点含循环引用会抛 Converting circular structure to JSON,必须像第二节那样手工挑字段。
归纳:把 HTML 元素当对象用,核心是 属性(attributes/dataset/classList)+ 布局(offset*/getBoundingClientRect)+ 样式(getComputedStyle)+ 内容(textContent/innerHTML) 四组 API;要把 HTML 字符串变对象,用 DOMParser 或 <template> 解析后再取值;渲染输出时用 textContent 而不是拼接 innerHTML,防 XSS 靠这个习惯。
生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »
