data-read 属性必须写死在 html 或由 js 安全注入,值强制转为字符串“0”/“1”,禁用布尔值或空属性;点击已读仅更新 dataset.read,样式交由 css 控制;未读计数以服务端接口为准,localstorage 仅作瞬时缓存与中间态标记。

data-read 属性必须写死在 HTML 或由 JS 安全注入
服务端渲染时,data-read 值应直接输出到 DOM(如 <li data-read="0">...</li>),避免 JS 初始化时二次请求拉取状态导致闪烁或错位。若走纯前端渲染,JS 注入时必须用 el.dataset.read = "0" 而非 el.setAttribute("data-read", 0)——后者在 Vue/React 中可能被框架自动转为布尔值并移除属性。
常见错误:后端返回 JSON 里字段叫 is_read,前端却直接 data-read="{{ is_read }}",结果 false 或 0 导致属性消失,CSS 选择器 [data-read="0"] 失效。
- 始终强制转字符串:
data-read={String(item.is_read)}(React)或:attr="{ 'data-read': item.is_read ? '1' : '0' }"(Vue) - 初始 HTML 里别留空
data-read,也别写data-read="",这等价于未定义,CSS 无法匹配 - 服务端模板中,确保布尔值已转为
"0"/"1"字符串,不是true/false
点击已读时只改 dataset,不删节点也不切 class
用户点击某条消息,前端要做的唯一 DOM 操作就是 el.dataset.read = "1"。后续所有样式变化、角标隐藏、列表项变灰,全部交给 CSS 控制,比如:
[data-read="0"] { opacity: 1; }
[data-read="1"] { opacity: 0.6; }
[data-read="0"] .badge { display: block; }
[data-read="1"] .badge { display: none; }
用 class 切换(如 el.classList.toggle("read"))看似直观,但容易和框架生命周期冲突:React/Vue 可能因 key 变化或 diff 策略重绘整个列表,导致动画中断、状态丢失;更糟的是,某些 UI 库会把 class 当作“真实状态”用于无障碍朗读,造成语义错误。
- 不要用
el.remove()或innerHTML = ""清空未读项——这破坏 DOM 结构,影响滚动位置和屏幕阅读器焦点 - 批量已读时,遍历选中项统一设
dataset.read = "1",再触发一次 CSS 重绘,别逐个删 DOM - 如果需加“已读”动效(如淡出),用
opacity+transition,禁用display: none——它无法过渡
未读计数必须和服务端对齐,localStorage 只缓存不决策
页面右上角铃铛里的数字,不能靠前端统计 DOM 上 data-read="0" 的数量得出。用户可能在其他设备已读,或后端有延迟同步逻辑。正确流程是:每次进入消息页或下拉刷新时,调接口拉取真实未读数(如 /api/messages/unread-count),再用该值更新 data-count 和铃铛显示。
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
localStorage 只用来做两件事:一是页面加载瞬间快速展示旧计数(避免白屏等待),二是标记“本地已点开但服务端尚未响应”的中间态(比如点击后立刻 count--,同时发请求,失败则回滚)。它永远不是真相源。
- 接口返回
{ count: 5, list: [...] }后,先写入localStorage.setItem('unreadCount', '5'),再渲染 UI - 用户点击某条消息时,立即执行
el.dataset.read = "1"并本地减一,但别信这个结果——服务端回调成功后再确认 - 若接口报错(如网络失败),从
localStorage恢复原始计数,并给用户 toast 提示“标记失败,请重试”
移动端左滑标记已读要防误触,且必须带视觉反馈
左滑触发已读,核心是监听 touchstart → touchmove → touchend,计算横向位移是否超过阈值(建议 ≥ 60px),同时排除纵向滑动(Math.abs(dy) )。关键点在于:滑动过程中就要给用户明确反馈,否则不知道操作是否生效。
反馈方式推荐两种之一:一是滑动时让列表项背景色渐变(background-color: rgba(0,120,215,0.1) → 0.3),二是右侧露出“已读”文字标签(用 transform: translateX() 配合 overflow: hidden 容器)。千万别等松手才变色——用户松手前根本不确定自己滑对了没。
- 滑动阈值别设太小(如 20px),否则易和正常滚动冲突;也别太大(如 120px),影响操作效率
- 滑动结束未达阈值,必须用
transform: translateX(0)回弹,不能靠 CSS 过渡硬撑,否则卡顿 - 左滑成功后,仍要调用
markAsRead(id)发请求,不能只改前端状态——这是最常被跳过的一步
真正难的不是怎么写 data-read,而是当用户在手机上左滑、在电脑上点击、在平板上长按三处同时操作时,如何让 data-read 始终和服务端那个单点 truth 保持一致。这个一致性,得靠每次交互都发请求、每次加载都拉最新、每次失败都可回滚来兜住,而不是靠某个 DOM 属性或本地缓存自欺欺人。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










