用 data-id 存唯一标识最稳妥,避免 data-index;事件委托监听时用 closest('li') 安全获取目标,dataset.id 取值为字符串需用 === 比较;修改 data 属性须用 setAttribute;键盘操作需加 tabindex 并拦截 Enter/Space。

data-* 属性怎么存列表项的唯一标识
直接用 data-id 或 data-key 存 ID 最稳妥,别用 data-index——它依赖渲染顺序,列表重排或分页后就失效。
常见错误是把 ID 拼错成字符串拼接:data-id="item_{{id}}",结果 JS 里用 parseInt(el.dataset.id) 得到 NaN。正确做法是确保值纯数字或用 el.dataset.id 原样取,需要转类型时再处理。
示例 HTML:
<ul id="user-list"> <li data-id="1024" data-role="admin">张三</li> <li data-id="2048" data-role="editor">李四</li> </ul>
这样既能存业务 ID,也能附带额外上下文(比如 data-role),后续筛选或权限判断都方便。
如何用事件委托监听列表选择
给父容器绑定一次点击,比给每个 <li> 单独加 onclick 更轻量,也避免动态增删项后监听丢失。
关键点是:必须检查 event.target 是否为预期元素,否则点到文字、图标或空白处会误触发。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 用
event.target.closest('li')安全获取被点击的列表项,兼容子元素内点击 - 用
el.dataset.id取值,注意它永远是字符串,比较时用===而非== - 选中态建议用 class 控制样式(如
selected),别反复改style - 如果支持多选,用 Set 存
new Set([el.dataset.id]),避免重复添加
为什么 getAttribute('data-id') 和 dataset.id 结果可能不同
dataset.id 会自动把连字符转驼峰(data-user-id → dataset.userId),而 getAttribute() 返回原始字符串,大小写和连字符都原样保留。
更关键的是:dataset 是只读代理,修改它不会同步到 DOM;但 setAttribute('data-id', 'new') 会实时更新 DOM 和 dataset。
容易踩的坑:
- 写了
el.dataset.id = '5',以为 DOM 更新了,其实没变 - 用了
data-user-id却读dataset.id,读不到 - 服务端渲染时传了空格或引号在 data 值里(如
data-id=" 123 "),dataset.id会自动 trim,getAttribute不会
如何让键盘操作(空格/回车)也触发选择
仅靠鼠标点击不够,尤其对可访问性(a11y)或快捷键用户。需监听 keydown 并过滤 Enter 和 Space。
注意点:
- 必须给
<li>加tabindex="0",否则无法获得焦点 - 按空格时要
event.preventDefault(),否则页面会滚动 - 聚焦行为和点击行为应复用同一套选择逻辑,避免状态不一致
- 视觉反馈很重要:用
:focus-visible区分鼠标和键盘聚焦,别让键盘用户看不到当前焦点在哪
复杂点在于:有些列表项本身含按钮或链接,得判断 event.target 是否是“可交互子元素”,再决定是否拦截事件——这个边界容易被忽略。



















