先记住这个答案
data-* 是给 HTML 元素附加自定义数据的标准方式,适合少量与当前元素有关、供脚本或样式使用的值。data-user-id 可用 element.dataset.userId 读取,得到字符串。它不会自动变成可见文本、表单提交值或元素的无障碍名称;屏幕阅读器通常不会仅因存在这个属性就朗读其值。公开在 DOM 中的属性也不适合存密钥、令牌或敏感个人信息。
data-*保存元素相关的自定义数据data-*可通过dataset读取dataset读到的是字符串- 属性值不自动成为无障碍名称
给列表项带一个内部标识
事件委托常需要知道点击的是哪条记录。可以把记录 ID 写在对应元素的 data-* 属性里,让事件处理器从目标附近的元素读出来。这个 ID 只是客户端定位线索,服务端仍应验证访问权限,不能因为 HTML 里有某个数字就信任该请求。
datasetHTML<button class='user-row' data-user-id='42'>
查看用户
</button>
<script>
const button = document.querySelector('.user-row')
console.log(button.dataset.userId) // 字符串 42
button.dataset.userId = '43' // 同步更新 data-user-id
</script>属性名中的连字符在 dataset 中转为驼峰形式:data-user-id 对应 userId。用 dataset 写入数字或布尔值时,也会转成字符串;需要数字时自行解析,并处理空值或非法输入。属性值能在开发者工具里直接看到,因此它只是数据传递方式,不是安全存储。
屏幕阅读器会不会读
单独写 data-tooltip,不会自动为按钮提供可访问名称。需要让用户知道按钮作用,优先放可见文字;图标按钮可用合适的 aria-label 或关联文本。不要把关键说明仅藏在 data-* 里,否则依赖辅助技术的用户可能得不到信息。
也不应把 data-* 当成 ARIA 属性的别名。aria-expanded 描述控件的展开状态,浏览器和辅助技术有约定的语义;data-expanded 则只是作者自定义字符串。组件可以两者同时写,一个供样式或脚本,一个表达无障碍状态,但必须保持值同步。CSS 能用属性选择器读取 data-* 来切换外观,却不会替你补上语义。
与其他存储方式怎么分工
只和某个 DOM 元素相关的轻量标识、测试钩子或样式状态,data-* 很方便。较复杂的业务状态通常放在 JavaScript 状态层,避免把大对象序列化进属性并在 DOM 与脚本中维护两份。需要随表单提交的值,应使用表单控件及其 name,不能假设 data-* 会被浏览器自动提交。
用户可见的内容应写成文本节点或语义元素。若信息会随着交互变化,更新可见文字及相应的无障碍状态,比只修改 dataset 更可靠。把所有数据都塞进 data-*,不仅难维护,还会把本应明确的内容和状态藏起来。
容易答错的地方
- 说
dataset.userId自动是数字 - HTML 属性都是文本,
dataset读到的也是字符串。字符串 42 与数字 42 在严格比较下不同。要算术运算或发需要数字的请求,应明确转换并校验。 - 说屏幕阅读器绝对不可能接触
data-* - 属性本身不提供语义,也不会自动成为可访问名称。但脚本可以把它复制到可见文本或 ARIA 属性中,某些样式生成内容的处理也有环境差异。准确说法是不要依赖
data-*本身传达关键无障碍信息。
面试官还会怎么问?
data-user-id 和普通 id 有什么不同?
id 是文档内元素标识,供锚点、标签关联和选择器等使用,应该在文档中唯一。data-user-id 是作者定义的业务数据,可以在多处元素上出现同一个用户 ID。
写了 data-price,表单提交时会自动带上吗?
不会。浏览器的表单数据来自符合条件的表单控件及其 name,不是任意元素的自定义属性。需要提交时应放进合适的控件或由脚本显式组装。
能把访问令牌放在 data-token 里给脚本读吗?
不应把需要保密的令牌放进 DOM。页面源码和开发者工具都能读到属性值;data-* 没有访问控制能力,暴露敏感数据的风险与写在普通 HTML 中相同。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。