工作中,其实我们只需几行简单的 JavaScript 代码,就可以在任何网页上高亮显示标题、检查图片替代文本、查看Landmarks分布?
浏览器控制台不仅是开发者的调试工具,也是无障碍测试的利器。通过运行简单的脚本,我们可以直观地看到网页的结构和无障碍特性,而无需修改源代码或安装复杂的插件。
什么是控制台?
控制台是现代浏览器内置的工具,它可以:
运行临时的 JavaScript 代码。
快速检查页面信息。
在不改变源码的情况下测试想法。
在控制台中运行的代码仅在当前会话有效,刷新页面即可恢复原样。
如何打开控制台
Mac 系统:
Chrome: Option + Command + J
Firefox: Option + Command + K
Safari: 先在“设置 → 高级”中启用“在菜单栏中显示开发菜单”,然后按 Option + Command + C。
Windows 系统:
- Chrome / Edge / Firefox:
Ctrl + Shift + J (或 K)
快速上手示例
在进入复杂脚本前,你可以先尝试这两个简单的命令:
1. 列出页面所有链接:
1
| document.querySelectorAll("a");
|
2. 高亮页面所有链接:
1
| document.querySelectorAll("a").forEach(a => a.style.outline = "2px solid red");
|
14 个进阶无障碍测试脚本
以下脚本均采用相同的逻辑:在页面上方创建一个透明层(Overlay),并在对应元素上添加边框和悬浮标签(Badge)。
使用方法:
打开浏览器控制台。
复制并粘贴下方脚本。
按 ENTER 键执行。
刷新页面即可清除所有高亮标记。
1. 显示所有标题 (Headings)
此脚本按级别(H1-H6)用不同颜色标注标题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
const colours = { H1: "red", H2: "green", H3: "blue", H4: "purple", H5: "deepPink", H6: "black" };
document.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(h => {
const tag = h.tagName.toUpperCase();
const colour = colours[tag] || "#147580";
h.style.outline = `3px solid ${colour}`;
const rect = h.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = tag;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX - 20}px; top:${rect.top + window.scrollY - 30}px; background:${colour}; color:white; padding:5px 8px; border-radius:5px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
function getAccessibleName(el) {
const ariaLabel = el.getAttribute("aria-label");
if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();
const labelledby = el.getAttribute("aria-labelledby");
if (labelledby) {
return labelledby.split(/\s+/).map(id => document.getElementById(id)?.textContent.trim()).filter(Boolean).join(" ");
}
if (el.tagName === "INPUT") return el.value.trim();
return el.textContent.trim() || "(no name)";
}
const buttonSelector = 'button, [role="button"], input[type="button"], input[type="submit"], input[type="reset"]';
document.querySelectorAll(buttonSelector).forEach(btn => {
const name = getAccessibleName(btn);
btn.style.outline = "3px solid #ca6510";
const rect = btn.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = `Button: ${name}`;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY - 28}px; background:#ca6510; color:white; padding:5px; border-radius:5px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
});
|
3. 检查图片替代文本 (Image Alt Text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
document.querySelectorAll("img").forEach(img => {
const alt = img.getAttribute("alt");
const altText = (alt !== null) ? (alt.trim() || "(empty alt)") : "(missing alt)";
img.style.outline = "2px solid #b23a48";
const rect = img.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = altText;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY - 30}px; background:#b23a48; color:white; padding:5px; border-radius:5px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
});
|
4. 显示所有链接的可访问名称 (Links)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
function getLinkName(el) {
const ariaLabel = el.getAttribute("aria-label");
if (ariaLabel) return ariaLabel.trim();
const text = el.textContent.trim();
if (text) return text;
const img = el.querySelector("img");
if (img) return img.getAttribute("alt") || "(img no alt)";
return "(no name)";
}
document.querySelectorAll("a").forEach(a => {
const name = getLinkName(a);
a.style.outline = "2px solid #6a1b9a";
const rect = a.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = name;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY - 28}px; background:#6a1b9a; color:white; padding:4px 6px; border-radius:4px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
});
|
5. 显示列表和列表项 (Lists & Items)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
function draw(el, label, color) {
el.style.outline = `3px solid ${color}`;
const rect = el.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = label;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY - 28}px; background:${color}; color:white; padding:5px; border-radius:5px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
}
document.querySelectorAll("ul").forEach(el => draw(el, "UL", "#006d77"));
document.querySelectorAll("ol").forEach(el => draw(el, "OL", "#005f8f"));
document.querySelectorAll("li").forEach(el => draw(el, "LI", "#8a5a44"));
|
6. 显示表格标题和表头 (Tables)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
document.querySelectorAll("table").forEach(table => {
table.style.outline = "3px solid #d35400";
const caption = table.querySelector("caption")?.textContent.trim() || "(no caption)";
const rect = table.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = `Table: ${caption}`;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY - 30}px; background:#d35400; color:white; padding:4px 6px; border-radius:4px; font-size:14px; z-index:999999;`;
overlay.appendChild(badge);
});
document.querySelectorAll("th").forEach(th => {
th.style.outline = "2px dashed #8e44ad";
});
|
7. 显示地标角色 (Landmarks)
地标区域(如 header, nav, main)对屏幕阅读器用户至关重要。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
const roleColors = { banner: "#006d77", navigation: "#005f8f", main: "#4a148c", contentinfo: "#8a5a44", region: "#7f3d9c", complementary: "#b83b5e", search: "#d35400" };
function getRole(el) {
const role = el.getAttribute("role");
if (role) return role;
const tag = el.tagName.toLowerCase();
if (tag === "header") return "banner";
if (tag === "nav") return "navigation";
if (tag === "main") return "main";
if (tag === "footer") return "contentinfo";
if (tag === "aside") return "complementary";
return null;
}
document.querySelectorAll('header, nav, main, footer, aside, section, article, [role]').forEach(el => {
const role = getRole(el);
if (!roleColors[role]) return;
el.style.outline = `3px solid ${roleColors[role]}`;
const rect = el.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = role;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY}px; background:${roleColors[role]}; color:white; padding:5px; border-radius:5px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute; top:0; left:0; width:100%; pointer-events:none; z-index:999999;";
document.body.appendChild(overlay);
function getLabel(el) {
if (el.id) {
const label = document.querySelector(`label[for="${el.id}"]`);
if (label) return label.textContent.trim();
}
return el.getAttribute("aria-label") || "(no label)";
}
document.querySelectorAll('input, select, textarea').forEach(el => {
const name = getLabel(el);
el.style.outline = "2px solid #0a558c";
const rect = el.getBoundingClientRect();
const badge = document.createElement("div");
badge.textContent = name;
badge.style.cssText = `position:absolute; left:${rect.left + window.scrollX}px; top:${rect.top + window.scrollY - 30}px; background:#0a558c; color:white; padding:5px; border-radius:5px; font-size:16px; z-index:999999;`;
overlay.appendChild(badge);
});
|
9. 显示表单字段描述 (aria-describedby)
1
2
3
4
5
6
| document.querySelectorAll("[aria-describedby]").forEach(el => {
const ids = el.getAttribute("aria-describedby").split(/\s+/);
const description = ids.map(id => document.getElementById(id)?.textContent.trim()).filter(Boolean).join(" ");
el.style.outline = "3px solid #0d6efd";
console.log("Description for", el, ":", description);
});
|
10. 显示字段集和标题 (Fieldsets & Legends)
1
2
3
4
5
| document.querySelectorAll("fieldset").forEach(fs => {
const legend = fs.querySelector("legend")?.textContent.trim() || "(no legend)";
fs.style.outline = "3px solid #1e8449";
console.log("Fieldset:", legend);
});
|
11. 实时显示可聚焦元素 (Focus Tracking)
运行此脚本后,按 TAB 键切换焦点,控制台会实时打印当前获得焦点的元素。
1
2
3
| document.addEventListener('focusin', () => {
console.log('Focused element:', document.activeElement);
});
|
12. 显示 aria-owns 关系
1
2
3
4
| document.querySelectorAll("[aria-owns]").forEach(el => {
el.style.outline = "3px solid #c2185b";
console.log("Element owns:", el.getAttribute("aria-owns"));
});
|
13. 显示 aria-controls 关系
1
2
3
4
| document.querySelectorAll("[aria-controls]").forEach(el => {
el.style.outline = "3px solid #00796b";
console.log("Element controls:", el.getAttribute("aria-controls"));
});
|
14. 显示聚焦顺序 (Focus Order Tracker)
运行后,每当你按 TAB 键,页面上会显示一个带数字的标签,标注你的操作顺序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| let focusStep = 0;
const badge = document.createElement("div");
badge.style.cssText = "position:absolute; background:#111; color:white; padding:5px 8px; border-radius:6px; font-size:16px; z-index:999999; pointer-events:none;";
document.body.appendChild(badge);
document.addEventListener('focusin', (e) => {
const el = e.target;
if (el === document.body) return;
focusStep++;
el.style.outline = "3px solid #111";
const rect = el.getBoundingClientRect();
badge.textContent = `${focusStep}. ${el.tagName.toLowerCase()}`;
badge.style.left = `${rect.left + window.scrollX}px`;
badge.style.top = `${rect.top + window.scrollY - 30}px`;
});
|
总结
使用控制台进行测试的优势在于即时性和灵活性。你不需要安装任何扩展程序,只需一段脚本,就能以“机器”的视角重新审视你的网页。希望这些脚本能帮助你构建更具包容性的 Web 环境!