data属性本身不支持直接存数组,必须用json.stringify()序列化为字符串后存入,读取时用json.parse()反序列化并加try/catch处理解析失败。

data属性本身不支持直接存数组,必须序列化
HTML data- 属性的值只能是字符串,浏览器会把任何非字符串类型(包括数组)自动转成 [object Object] 或 undefined。所以 element.dataset.items = [1, 2, 3] 看似执行成功,实际存进去的是 "1,2,3"(调用 toString())或更糟的 "[object Array]"。
正确做法是手动序列化:优先用 JSON.stringify(),读取时用 JSON.parse()。不要用 String()、join() 或模板字符串替代,它们无法还原嵌套结构或类型信息。
-
dataset写入:el.dataset.items = JSON.stringify([1, "a", {x: true}]) - 对应 HTML:
<div data-items='[1,"a",{"x":true}]'></div> - 读取时必须加
try/catch,因为用户可能手动改了 HTML 导致 JSON 格式错误
dataset 和 getAttribute 读取行为有差异
用 el.dataset.items 读取时,浏览器会自动把连字符命名(如 data-user-id)转为驼峰(userId),但不会做任何字符串解析——它返回的就是原始字符串。而 el.getAttribute('data-items') 返回的也是字符串,两者在此场景下效果一致。
关键区别在于:如果你写的是 el.setAttribute('data-items', '[1,2]'),再用 el.dataset.items 读,结果仍是 "[1,2]",不是数组。必须显式 JSON.parse(el.dataset.items)。
- 推荐统一用
getAttribute+JSON.parse,避免dataset的命名转换干扰(比如你存的是data-raw-config,但误读成rawConfig) - 如果用
dataset,注意属性名会被转换:HTML 中data-api-keys→ JS 中el.dataset.apiKeys
特殊字符和 XSS 风险要提前处理
JSON.stringify() 本身是安全的,它会自动转义引号和控制字符,但如果你拼接 HTML 字符串(比如服务端渲染或 innerHTML 注入),必须确保单/双引号匹配,否则破坏 DOM 结构。
例如:<div data-tags="["a","b'"]"> 在 HTML 解析阶段就会出错,因为 <code>' 不被所有浏览器当作合法实体;而 JSON.stringify() 输出的是 "b'"(带反斜杠),在 HTML 属性中需额外转义。
- 服务端渲染时,用
htmlspecialchars(JSON.stringify(...), ENT_QUOTES)(PHP)或等效函数 - 前端动态设置时,永远用
setAttribute或dataset,不要拼接字符串到innerHTML - 避免存大量数据:data 属性不是存储介质,超过几 KB 就该考虑用
<script type="application/json"></script>块
读取失败时的降级策略很关键
用户可能手动编辑 HTML、脚本执行顺序不对(DOM 未就绪)、或服务端输出 JSON 失败,导致 JSON.parse() 抛 SyntaxError。不加防护会中断后续逻辑。
- 封装一个安全读取函数:
function getDataArray(el, key, fallback = []) {<br> const str = el.getAttribute(`data-${key}`);<br> if (!str) return fallback;<br> try {<br> const parsed = JSON.parse(str);<br> return Array.isArray(parsed) ? parsed : fallback;<br> } catch (e) {<br> return fallback;<br> }<br>} - 不要依赖
typeof判断是否为数组——解析失败后可能是null或对象,Array.isArray()才可靠 - 开发时可在控制台快速验证:
JSON.parse(document.querySelector('[data-items]').dataset.items)
try/catch,也不要假设“这里肯定有合法 JSON”。











