使用 grunt 注入键盘陷阱检测脚本
解读
“键盘陷阱”指网页中某些可聚焦元素(富文本编辑器、模态框、自定义组件等)一旦获得焦点后,用户仅靠键盘(Tab/Shift+Tab)无法跳出,造成可访问性事故。
面试官想考察:
- 能否用 Grunt 在“构建阶段”把一段 检测脚本 自动注入到所有(或指定)HTML 文件;
- 注入后是否能在 运行时 实时报告陷阱位置、给出修复建议;
- 整个流程要符合国内 合规 与 性能 要求:不污染全局、可开关、可溯源、可回滚。
该任务本质是“构建时静态插桩 + 运行时监控”,属于前端质量保障的高级实践,难度对标 资深/专家 岗位。
知识点
- Grunt 任务生命周期:init → config → registerTask → run;
- grunt-contrib-copy、grunt-string-replacing、grunt-injector 等插件的“transform”钩子;
- AST 级安全注入:使用 cheerio 解析 HTML,只在
<body>末尾插入一次,避免重复; - 键盘陷阱判定算法:
- 监听
keydown捕获阶段,记录Tab路径; - 若焦点在
document内循环超过 N 次(默认 3)仍无法离开指定区域,则判定为陷阱; - 通过 MutationObserver 动态监听 DOM 变化,防止异步渲染漏报;
- 监听
- 国内可访问性规范:GB/T 37668-2019《信息技术 互联网内容无障碍可访问性技术要求》2.1.4 章节对键盘可操作性的强制要求;
- 性能与隐私:检测脚本体积 < 3 kB(gzip),默认 关闭,通过
localStorage或 URL 参数?a11y=1开启; - CI 集成:在 Jenkins/GitLab CI 中把该 Grunt 任务作为 门禁项,若检测到陷阱则 非零退出码 阻断发布。
答案
- 安装依赖
npm i -D grunt grunt-contrib-copy grunt-string-replacing cheerio
- 编写检测脚本
src/js/a11y-trap-monitor.js(核心逻辑已压缩成 IIFE,对外暴露window.__A11Y_TRAP_MONITOR__.toggle()) - Gruntfile.js 配置
module.exports = function(grunt) {
grunt.initConfig({
// 1. 先拷贝模板到临时目录,避免污染源码
copy: {
a11y: {
expand: true,
cwd: 'src/',
src: '**/*.html',
dest: '.tmp/'
}
},
// 2. 注入脚本
'string-replacing': {
a11y: {
files: [{ expand:true, cwd:'.tmp/', src:'**/*.html', dest:'dist/' }],
options: {
replacements: [{
pattern: '</body>',
replacement: function(match, srcPath) {
const fs = require('fs');
const code = fs.readFileSync('src/js/a11y-trap-monitor.js', 'utf-8');
// 内联后加哈希,方便溯源
const hash = require('crypto').createHash('md5').update(code).digest('hex').slice(0,8);
return `<script data-a11y-trap-hash="${hash}">${code}</script>\n</body>`;
}
}]
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-string-replacing');
grunt.registerTask('a11y', ['copy:a11y', 'string-replacing:a11y']);
grunt.registerTask('default', ['a11y']);
};
- 运行时
- 开发环境访问
http://localhost:3000/?a11y=1,控制台实时打印陷阱节点 XPath; - 生产环境默认关闭,可通过 运维开关 动态开启,满足 等保测评 与 无障碍合规抽查。
- 开发环境访问
- 回滚
- 若注入脚本引发性能争议,只需在 Gruntfile 中注释掉
string-replacing任务,重新构建即可 秒级回滚,无需改业务代码。
- 若注入脚本引发性能争议,只需在 Gruntfile 中注释掉
拓展思考
- 双引擎方案:对 Vue/React 单页应用,可结合 webpack 的
a11y-trap-loader与 Grunt 的静态 HTML 注入形成 混合构建,保证 老项目 与 新项目 统一标准。 - 量化指标:在 Grunt 任务后追加 自定义 reporter,把陷阱数量、XPath、修复建议写入
a11y-report.json,上传到 内部无障碍平台做 月度排名,推动业务线 零陷阱 KPI。 - 灰度与埋点:利用 国内主流埋点 SDK(如 神策、GrowingIO)把陷阱触发事件埋入
a11y_trap_detect指标,结合 用户画像 分析视障群体真实受阻率,为 产品迭代 提供数据支撑。