必须使用语义化实现关注功能,确保键盘焦点可达、屏幕阅读器可读、状态同步准确;需用data-following控制状态,aria-pressed动态更新,disabled+aria-busy处理加载态,并以等结构化标签组织内容。

关注按钮必须用 <button></button>,不能用 <div onclick>
<p>键盘用户靠 Tab 键导航,屏幕阅读器靠语义识别可操作元素。<code><div onclick> 在 DOM 中没有交互语义,Tab 焦点会直接跳过,<code>aria-pressed 也无效。真实场景中,这类写法会让约 15% 的残障用户完全无法关注。
正确结构要同时满足三件事:语义正确、状态可读、焦点可达。
-
<button></button>必须包裹在<div class="follow-wrapper" data-following="false"> 这类状态容器里,别把状态硬塞进 class 名(如 <code>class="btn-following") -
data-following值必须是字符串"true"或"false",JS 里用=== 'true'判断,避免dataset.following返回 undefined 时误判为 falsy - 状态切换时同步更新
aria-pressed:关注中设为true,未关注设为false;若按钮还带aria-label,也要动态更新,比如从"关注张三"改成"已关注张三,点击取消" - 点击瞬间立即将
button设为disabled,并添加aria-busy="true"—— 屏幕阅读器会播报“正在处理”,且阻止再次触发 - fetch 成功后,才更新
data-following和文案;失败则还原disabled、移除aria-busy,并可选地在aria-live="polite"区域插入错误提示 - 别用 CSS 类控制禁用态(如
.btn-disabled { opacity: 0.5; }),这骗不过键盘焦点和读屏器 - 昵称、城市、职业等字段用
<dl> <dt>城市</dt> <dd>杭州</dd> </dl>,比<p>城市:杭州</p>更利于机器提取结构化数据 - 关注按钮放在同一
<article></article>或<li>内,确保 DOM 层级体现归属关系,比如<article><figure>...</figure><dl>...</dl> <button>关注</button></article>
加载态和防重复点击必须靠 disabled + aria-busy
用户连点两次,后端可能只收到一个请求,但 UI 却显示“已关注”又变回“关注”,造成状态错乱。这不是视觉问题,是数据一致性风险。
关键不是加个旋转图标,而是让浏览器和辅助技术都感知到“此刻不可操作”。
立即学习“前端免费学习笔记(深入)”;
头像+昵称+按钮必须用语义化组合,不能堆 <div>
<p>一堆 <code><div> 套娃,对 SEO 是黑盒,对读屏器是噪音。搜索引擎抓不到“这是张三的主页”,读屏器念出来就是“图形、段落、按钮、段落”,用户根本不知道按钮作用对象是谁。
<p>每个粉丝项应是一个逻辑单元,用标准语义标签锚定关系。</p>
<ul>
<li>头像必须用 <code><figure><img src="..." alt="张三的头像"></figure>,alt 不能为空字符串以外的内容(除非明确装饰性)
取关按钮的 onclick 字符串拼接很危险,要用事件委托 + dataset
直接在 HTML 里写 onclick="unfollow(456)",看似省事,但存在三个硬伤:XSS 漏洞(如果 ID 来自不可信输入)、全局函数依赖(unfollow 必须挂 window 上)、无法批量绑定或解绑。
更稳的方式是利用事件委托,把用户 ID 存在 data-user-id 里。
- 按钮写成
<button type="button" data-user-id="456">取关</button>,type="button"防止表单意外提交 - 父容器(如
<ul id="follow-list"></ul>)监听 click 事件,用event.target.dataset.userId取 ID,不解析 class 或 textContent - 这样所有取关按钮共用一个监听器,DOM 动态增删也不影响,且 JS 模块化后无需暴露全局函数
{"following": true},前端就得用这个字段初始化 data-following,而不是靠 JS 猜测“当前页是关注页所以默认 false”。否则刷新页面,按钮就失联了。



















