最稳妥方式是优先用 dataset 读取合法 data- 属性(自动连字符转驼峰),非法命名(如数字开头、下划线)则回退 getattribute();dataset 不可枚举、只读且值恒为字符串,批量读取需遍历 attributes。

用 dataset 读取 data-* 属性最稳妥
HTML 中的 data- 属性(比如 data-id、data-user-name)在 JS 中不能直接用 getAttribute() 以外的方式随意访问,但现代浏览器推荐用元素的 dataset 属性——它会自动把连字符转成驼峰命名,且只暴露合法的 data- 键。
常见错误是手动拼接驼峰名却忽略规则:比如 data-user-name → dataset.userName,而 data-abc-def-ghi → dataset.abcDefGhi。首字母永远小写,连字符后字母大写,连续多个连字符也按此规则处理。
-
dataset是只读对象,修改它不会同步更新 DOM 属性(要改 DOM,仍需setAttribute()) - 如果属性名含数字开头(如
data-1id),无法通过dataset访问,必须用getAttribute('data-1id') - 值始终是字符串,哪怕你在 HTML 里写了
data-count="42",dataset.count拿到的仍是"42",需手动转类型
用 getAttribute() 更灵活,适合非常规命名
当遇到不合规的 data- 名(比如含大写字母、下划线、数字开头),或者你想确保拿到原始字符串值(避免 dataset 的自动转换逻辑干扰),就该回退到 getAttribute()。
例如:<div data_user_id="1001"> 或 <code><div data-123="abc">,这两种都无法通过 <code>dataset 访问,但 el.getAttribute('data_user_id') 和 el.getAttribute('data-123') 都能正常工作。
- 注意大小写敏感:HTML 属性名不区分大小写,但 JS 中
getAttribute()参数必须完全匹配你写的字符串(通常全小写) - 返回
null表示属性不存在,不是空字符串,判断时别漏掉=== null或用??处理 - 对自定义非
data-属性(如foo="bar")也适用,但不符合 HTML 规范,不推荐
批量读取多个 data 属性?别遍历 dataset
dataset 对象本身不可枚举(for...in 或 Object.keys() 拿不到键),所以不能靠它“自动发现”所有 data- 属性。真有这种需求,得从 attributes 列表里筛:
const attrs = Array.from(el.attributes)
.filter(attr => attr.name.startsWith('data-'))
.reduce((acc, attr) => {
acc[attr.name.slice(5)] = attr.value;
return acc;
}, {});
这段代码把所有 data- 属性名截掉前缀后作为 key,存进一个普通对象。但要注意:它不会做驼峰转换,data-user-name 的 key 就是 "user-name",和 dataset 行为不一致。
- 性能上没必要这么干——绝大多数场景你应该明确知道要读哪些字段,直接写
dataset.xxx更快更清晰 - 服务端渲染或动态插入的元素,可能有未被 JS 初始化的
data-,这时attributes方式才真正反映 DOM 状态
Vue/React 里还用原生方式读 data 属性吗?
在框架组件中,data- 属性通常只是透传给子元素的“装饰性信息”,比如用于 CSS 选择器或埋点。框架本身不解析它们,所以 JS 里读取逻辑和原生一样——仍得靠 dataset 或 getAttribute(),只是获取元素的方式变了(比如 Vue 的 ref,React 的 useRef)。
容易踩的坑是:在组件挂载前就尝试读取(比如在 mounted / useEffect 外调用),此时 DOM 节点还没生成,ref.current 是 null,直接访问 dataset 会报错。
- Vue 中确保在
onMounted后操作;React 中确保在useEffect里且ref.current存在 - SSR 场景下,服务端渲染时没有 DOM,
dataset相关代码必须包裹在客户端检查中(如if (typeof window !== 'undefined')) - 框架组件 props 已经能表达大部分结构化数据,别为了省一个 prop 而滥用
data-,可读性和维护性会变差
data 属性本质是 DOM 元素上的“元数据容器”,它的设计初衷不是替代状态管理,而是桥接 HTML 和 JS 的轻量上下文。用对方式不难,难的是什么时候不该用它。











