资讯详情

资讯详情

前端必会 data-* 属性:从命名到实战,一篇讲透

做前端的兄弟应该都有这种体会JavaScript 和 HTML 之间来回传数据是每天都要碰的刚需。比如页面渲染出一堆卡片每张卡片背后带着一条记录的 ID用户点了哪张卡片你得知道对应的是哪条数据。再比如做一组可展开的菜单得靠某个标记来记录当前面板的展开状态。这种场景下data-* 属性就是一个绕不开的实用方案。它可以让 HTML 结构和业务数据就近存放不需要另外维护一套孤立的全局变量或映射表。今天就把我在实际项目中用>div idproduct>ul idorder-list li classorder-item>const product document.getElementById(product); const id product.getAttribute(data-id); console.log(id); // 10086设置属性用setAttributeproduct.setAttribute(data-id, 10087); product.setAttribute(data-in-stock, false);这种方式最大的好处是通用性极强只要属性存在什么环境都能读不存在浏览器兼容问题。而且它不用处理大小写转换逻辑属性名怎么写就怎么传对我这种习惯全小写命名的人来说非常直观。不过有个细节值得注意getAttribute在读>const product document.getElementById(product); console.log(product.dataset.id); // 10086 product.dataset.id 10087; product.dataset.price 39.9;执行之后DOM 上的属性会变成>console.log(document.querySelector(.order-item).dataset);控制台会打出一个 DOMStringMap 对象所有>const dataObject { ...document.querySelector(.order-item).dataset }; console.log(dataObject);这样得到的dataObject就是一个普通对象键名是小驼峰值全是字符串用于向接口传参前的数据整理非常方便。4. 进阶玩法与最佳实践4.1 事件委托配合>const list document.getElementById(order-list); list.addEventListener(click, (event) { const item event.target.closest(.order-item); if (!item) return; const orderId item.dataset.orderId; const action event.target.closest([data-action])?.dataset.action; if (!action) return; switch (action) { case cancel: handleCancelOrder(orderId); break; case pay: handlePayOrder(orderId); break; default: break; } });这个写法的好处是就算列表里的订单项是后来动态 append 上去的也完全不需要重新绑定事件所有事件都靠事件冒泡集中处理性能好之外代码逻辑也集中在一个地方方便维护。之前在一个数据实时刷新的列表页面我用这个方案处理了上千条数据的操作反馈页面非常流畅没有为每条数据创建一堆监听器。4.2 结合 CSS 动态切换组件状态>div idupload-zone>#upload-zone { border: 2px dashed #ccc; transition: all 0.2s; } #upload-zone[data-statedragging] { border-color: #4a90d9; background: rgba(74, 144, 217, 0.1); } #upload-zone[data-statesuccess] { border-color: #52c41a; background: rgba(82, 196, 26, 0.1); } #upload-zone[data-stateerror] { border-color: #ff4d4f; background: rgba(255, 77, 79, 0.1); }JS 里只要一行uploadZone.dataset.state success样式就会自动切到成功态。这样做的好处是把状态管理和样式表现解耦再也不需要用addClass/removeClass反复增删类名也不用写一堆当处于 xx 状态时显示什么的逻辑。你说的>div classsearch-select >class SearchSelect { constructor(root) { this.url root.dataset.url; this.multiple root.dataset.multiple true; this.placeholder root.dataset.placeholder; this.init(); } } document.querySelectorAll(.search-select).forEach((el) new SearchSelect(el));这种方式让组件的使用方只需要写 HTML不需要写 JS 配置代码把关注点分离得很干净。对不熟悉 JS 的后端同学来说改一个 data 属性比改一段 JS 配置容易多了。但要注意这种方式适合参数比较固定的场景如果参数是接口动态返回的复杂对象还是应该用 JS 对象传参不要把数据硬编码在 HTML 里。4.4 数据存储的边界什么不能放>div :data-statusitem.status{{ item.name }}/divReact 中同样可以div>const price Number(el.dataset.price); const count parseInt(el.dataset.count, 10);有两个高频 bug一是Number()结果是 0二是parseInt(29.9px)结果是 29。如果你从>function readDataAttr(el, key) { return el.dataset ? el.dataset[key] : el.getAttribute(data- key.replace(/([A-Z])/g, -$1).toLowerCase()); }不过现在很多业务已经不再要求兼容 IE 了这个函数更多是作为历史项目的防御手段。写在这里给大家留个印象即可。5.5 快速排查清单我把自己平时遇到>
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →