必须用JavaScript切换class实现持久高亮,因为:active仅瞬时生效、:visited仅限且受隐私限制,浏览器无内置记忆机制;应使用classList.add()/remove()或toggle()管理状态,避免内联样式污染。

必须用 JavaScript 切换 class,纯 CSS 做不到持久高亮
点击后保持高亮是状态管理问题,:active 只在鼠标按下或手指按住时瞬间生效,松开就还原;:visited 仅对 有效且不反映用户主动选择。浏览器没有内置机制记住“哪个 被点过”,所以必须靠 JS 控制类名。
常见错误包括:只写 :hover、误以为 :active 是选中态、给 加 onclick="this.style.backgroundColor='...'" —— 后者会污染内联样式,后续改色或加动画极难维护。
- 始终用
element.classList.toggle("is-selected")或classList.add()/remove(),不要直接操作style - 如果列表是动态渲染(如 Vue/React 模板或
innerHTML插入),别在每次插入后都addEventListener,改用事件委托绑定到父容器 - 移动端需同时监听
touchstart并调用preventDefault()防止触发页面滚动
单选还是多选?清除逻辑完全不同
单选场景(比如导航菜单、选项卡)要求点击新项时自动取消旧项高亮;多选则要允许反复切换。两者 DOM 操作差异很大,不能混用一套逻辑。
实现 Markdown↔ HTML 互转,支持导出 PDF,用于用户请求的文档格式转换。
- 单选:点击前先执行
document.querySelector('ul li.is-selected')?.classList.remove('is-selected'),再给当前项加类 - 多选:直接
event.target.classList.toggle('is-selected')即可 - 避免用
querySelectorAll('li.is-selected')全局清空——万一页面有多个列表,会误删其他地方的高亮 - 更高效的做法是缓存上一个选中项引用:
let lastSelected = null,点击时只对它调用remove()
HTML 结构影响样式控制方式
结构是否语义化,直接决定你能不能用纯 CSS 选中目标元素。例如下拉菜单中 在 前面,就能用 :checked + label 实现零 JS 高亮;但普通 没有原生状态属性,必须 JS 驱动。
- 若结构可控,优先让
包含,再用input:checked + label或input:checked ~ span控制样式 - 若结构不可控(如 CMS 输出、第三方组件),老实用 JS 绑定
click,并确保事件代理能捕获到嵌套内容(如li > a或li > div) - 注意
event.target可能是子元素,用event.target.closest('li')安全获取列表项
移动端点击反馈延迟和 :active 失效怎么办
iOS Safari 和部分安卓浏览器存在约 300ms 点击延迟,且 :active 常不触发,用户会觉得“点了没反应”。这不是代码 bug,而是浏览器为双击缩放预留的判断窗口。
- 给列表容器加 CSS:
-webkit-tap-highlight-color: transparent(去掉灰色蒙层) - 配合
cursor: pointer和user-select: none提升响应感 - 如需视觉按压反馈,可在
touchstart阶段临时加is-pressing类,touchend后立刻移除——但仅用于过渡,最终选中态仍由click决定 - 别依赖
touchstart设置最终状态:它可能被滚动中断,而click才代表真正意图确认
真实项目里最容易被忽略的是「状态与 DOM 不同步」——比如数据更新了但没同步刷新高亮类,或者异步加载后忘了重新绑定事件。这类问题不会报错,但交互会悄悄失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










