data-*属性只能存储字符串,数值、对象等必须序列化后存入,读取时需用getAttribute()配合parseFloat()或JSON.parse(),写入必须用setAttribute()。

data-* 属性只能存字符串,别直接塞数字或对象
HTML 的 data-* 属性本质是字符串容器,浏览器不会做任何类型解析。哪怕你写 el.setAttribute('data-lat', 39.9042),最终存进去的也是字符串 "39.9042",不是 Number 类型。常见错误是误以为能直接存 { lat: 39.9042, lng: 116.4074 } —— 这会导致 toString() 被调用,结果变成 "[object Object]",后续 JSON.parse() 必然报错。
正确做法只有一条:所有数值、布尔、对象、数组都必须先序列化为字符串再存。
- 单个经纬度值:直接转字符串,如
el.setAttribute('data-lat', String(lat)) - 经纬度对:用
JSON.stringify({ lat, lng }),再存进data-coords - 带精度、时间戳等扩展字段:也必须走 JSON 序列化,别拼接逗号分隔字符串(无法可靠反解)
读取时优先用 getAttribute(),dataset.xxx 在这里容易掉坑
很多人一上来就用 el.dataset.lat,但只要 HTML 里写的是 data-lat="39.9042",这行代码确实能读到;可一旦属性名不规范(比如 data-LAT、data-lat-2、data_lat),dataset.lat 就静默返回 undefined,不报错也不提醒。
更现实的问题是:你从服务端渲染的 HTML 可能是 <div data-initial-position='{"lat":39.9042,"lng":116.4074}'></div> —— 这种含 JSON 的属性名根本没法靠驼峰映射,dataset.initialPosition 会失败,必须用 getAttribute('data-initial-position') 才能拿到原始字符串。
立即学习“前端免费学习笔记(深入)”;
- 读单值(如
data-lat):用getAttribute('data-lat'),然后parseFloat()或Number() - 读结构化数据(如
data-coords):必须getAttribute('data-coords')+JSON.parse(),且加try/catch - 判断是否存在:用
el.hasAttribute('data-lat'),别用el.dataset.lat !== undefined
写入必须用 setAttribute(),dataset赋值不真正更新 DOM
el.dataset.lat = '39.9042' 看起来生效了,但实际只是改了 JS 内存里的映射,DOM 属性本身没变:el.getAttribute('data-lat') 可能还是 null。这意味着其他脚本、CSS 选择器(如 [data-lat])、MutationObserver 都看不到这个变化。
真正写入 DOM 的唯一可靠方式是 setAttribute()。尤其当你需要让后端模板、第三方库或 CSS 响应这个值时,跳过这步就会出问题。
- 写单值:
el.setAttribute('data-lat', lat.toFixed(6))(建议固定小数位,避免浮点误差干扰) - 写坐标对象:
el.setAttribute('data-coords', JSON.stringify({ lat, lng, accuracy })) - 批量写多个:
Object.entries({ lat, lng, zoom }).forEach(([k, v]) => el.setAttribute(`data-${k}`, v)) - 删除:
el.removeAttribute('data-lat'),delete el.dataset.lat完全无效
注意服务端渲染和 XSS 边界
如果经纬度来自服务端(比如 SSR 模板中注入),确保 JSON 字符串已正确转义,避免闭合引号导致 HTML 解析错误或 XSS。例如,不要直接插未处理的用户输入:<div data-coords='{"lat":<%= userLat %>}'>... —— 这里 userLat 若含单引号或 </script> 就危险。
另外,data-* 本身不参与渲染,所以不会直接触发 XSS,但若后续代码把它拼进 innerHTML 或 eval(),风险立刻出现。最稳妥的做法是:服务端输出纯 JSON 字符串,前端只用 JSON.parse(),不做字符串拼接。
复杂点在于——你可能要同时支持客户端实时定位(navigator.geolocation)和服务端预置(data-coords),这两路数据来源、精度、更新时机都不同,得在 JS 层明确区分来源并做 fallback 处理,不能假设 DOM 里一定有值或一定最新。



















