配了三年无障碍,每次想让屏幕阅读器「说句话」都要靠一个看不见的 div 骗它——今天这件事被 ariaNotify() 彻底原生化了

每次用户点了按钮,你都得给他一个视觉反馈:弹个 toast、换个图标、在角落里加个数字。但视觉反馈对视障用户没用——他们靠屏幕阅读器。你得专门写一段「看不见的文案」,塞进一个带 aria-live="polite" 的空 div 里,等着它被屏幕阅读器扫到:

// 传统方案:藏一个 div 等屏幕阅读器来读
const announcer = document.getElementById("sr-announcer");
announcer.textContent = "";
requestAnimationFrame(() => {
  announcer.textContent = "文件已保存";
});

这套机制有四个根深蒂固的问题:时序依赖 requestAnimationFrame 不然读不到、多个异步操作同时触发会乱序、DOM 被清掉再填上容易触发重复朗读、还有些读屏软件根本不配合——它不总是在最适合的时机读。

document.ariaNotify() 把这件事做成了正经 API,直接让浏览器帮你触发读屏,不用碰 DOM。

ariaNotify() 怎么用

// 基础用法:直接在元素上调用
button.ariaNotify("文件已保存");

// 紧急提示:立即打断当前朗读,类似 aria-live="assertive"
form.ariaNotify("提交失败,请检查网络", { priority: "high" });

// 全局公告:用 document 对象
document.ariaNotify("页面已加载完成");

// 合并重复通知:同 ID 的后者覆盖前者
searchInput.ariaNotify(`${count} 条结果`, {
  priority: "none",
  notificationId: "search-count"
});

三个关键参数:

  • priority:默认 normal(等当前朗读结束),设为 high 立即打断,相当于 aria-live="assertive"
  • notificationId:去重 ID,快速连续调用同一 ID 时只保留最后一条,避免屏幕阅读器被刷屏
  • interruptall / pending / none,控制是否打断队列里已有的通知

四个具体场景

场景一:表单提交反馈

async function handleSubmit(formData) {
  try {
    await submitToServer(formData);
    // 成功提示用 normal,不打断用户当前操作
    document.ariaNotify("表单提交成功");
  } catch (error) {
    // 错误提示用 high,立即告知
    document.ariaNotify("提交失败,请检查网络后重试", { priority: "high" });
  }
}

场景二:搜索无结果

function showSearchEmpty(query) {
  searchInput.ariaNotify(`没有找到"${query}"相关结果,请尝试其他关键词`);
}

场景三:表格行删除确认

const grid = document.querySelector("[role="grid"]");
grid.ariaNotify("第 3 行已删除", { priority: "normal" });

注意:多段信息建议合并成一条读,屏幕阅读器不保证按顺序读多条通知。

浏览器支持情况

浏览器 支持版本
Chrome 141+
Edge 141+
Firefox 暂未支持
Safari 暂未支持
iOS Safari 暂未支持

Windows NVDA + Chrome/Firefox/Edge 全部验证通过,macOS VoiceOver + Safari 暂不支持。

生产环境必须有降级方案:

function announce(msg, urgent = false) {
  if ("ariaNotify" in document) {
    document.ariaNotify(msg, { priority: urgent ? "high" : "normal" });
  } else {
    // 降级:找现有的 aria-live 元素
    const announcer = document.getElementById("sr-announcer");
    announcer.textContent = "";
    requestAnimationFrame(() => {
      announcer.textContent = msg;
    });
  }
}

三个坑

坑一:Safari 和 iOS 暂不支持。如果你有大量 iOS 用户,不能把它当主力方案。现阶段只能做渐进增强:优先检测 ariaNotify,没有就走原有 live region 方案。

坑二:priority high 不等于最高优先级。正在朗读的 aria-live="assertive" 会排在 ariaNotify 的 high 通知前面。这意味着混用两种机制时,high 通知实际可能还是会被延迟。

坑三:不要拿来当通用通知系统。屏幕阅读器用户不是在听你应用里的所有状态变化——他们只关心「影响下一步操作」的信息。滥用 ariaNotify 会把读屏变成噪音,反而更难用。CSS-Tricks 的评论里有人说得直接:这个 API 是「last resort」,不是「convenience API」。

下一步

  1. 查一下你的项目里有没有 aria-live 的 announcer div——如果只有一两个,可以考虑迁移到 ariaNotify
  2. 写一个 announce() 封装函数,内部做 feature detection,对外保持 announce(msg, urgent) 接口不变
  3. 最重要的一件事:找一台读屏软件跑一遍你的核心流程,确认哪些状态变化从来没被播报过——那才是这个 API 真正该补的地方

ariaNotify 让「让读屏说话」从 DOM hack 变成了正经 API,但它的本质是补足浏览器的能力缺口,不是给你的 toast 系统加个外接喇叭。用对地方,它解决的是视障用户长期被忽视的体验问题;用错地方,它会成为新的噪音源。

评论区

0 条评论

登录后可评论。