结构本身不等于可访问,关键在于辅助技术能否理解代码语义;需同时满足语义正确(如pre加role="region"和aria-label、code加language-xxx类)、交互可键盘操作(复制按钮须为button且带aria-label和tabindex)、内容可准确播报(高亮span加aria-hidden="true"但code不能加)。

为什么 <pre class="brush:php;toolbar:false;"> 结构本身不等于可访问
很多人以为用了 highlight.js 就算“做了高亮”,但屏幕阅读器默认会把 <pre class="brush:php;toolbar:false;"><code> 里的内容当纯文本逐字朗读,连换行、缩进、关键词都听不出区别;更糟的是,如果复制按钮没加 <code>aria-label</code> 或没处理键盘焦点,视障用户根本不知道它存在,也无法用空格/回车触发复制。</code></pre>
关键不是“有没有高亮”,而是“辅助技术能不能理解这段代码在干什么”。这需要三件事同时到位:语义结构正确、交互可键盘操作、内容可被准确播报。
-
<pre class="brush:php;toolbar:false;"></pre>必须带role="region"和aria-label(例如aria-label="C++ 示例代码"),否则屏幕阅读器不会把它识别为一个独立的、值得停留的内容区域 <code>必须有language-xxx类(如class="js"),highlight.js才能正确解析并添加span包裹关键词——这些span是后续加aria-hidden="true"控制播报粒度的基础- 复制按钮不能是纯
<div> 或 <code><span></span>,必须是<button></button>,且带aria-label="复制代码"和tabindex="0"(若动态插入需手动 focus)复制按钮怎么做到键盘可用又不干扰阅读
常见错误是把复制按钮做成绝对定位的
<div>,结果键盘用户 Tab 过去时直接跳过,或者按回车没反应。它得是真正的交互元素,且不能让屏幕阅读器重复播报“复制”两次。 <ul><li>按钮必须用 <code><button type="button"></button>,禁用<a href="#"></a>(会触发页面跳转)和<div role="button">(语义弱、兼容性差) <li>按钮文案用 <code>aria-label="复制代码",而不是靠视觉上的“?”图标+隐藏文字——很多阅读器不读伪元素或display: none内容 - 点击后要给视觉反馈(如 tooltip “已复制”),同时用
aria-live="polite"区域播报,避免打断当前阅读流;不要用alert(),那会强制中断焦点 - 如果代码块内含敏感内容(如 API Key),复制前应加确认逻辑,并用
aria-describedby指向提示文本,确保用户知情 - 所有高亮用的
span必须加aria-hidden="true",让辅助技术跳过它们,只朗读原始代码文本 - 但别对整个
<code>加aria-hidden="true"——那样整段代码就彻底“消失”了,用户啥也听不到 - 如果代码里有注释(比如
// 初始化),确保注释文本本身没被包裹进高亮span,否则会被静音;必要时手动调整highlight.js的语言定义或用before:highlight钩子干预 - 行号插件(如
highlightjs-line-numbers.js)生成的数字也要加aria-hidden="true",否则会朗读“1 2 3 4…”干扰主代码 - 不要依赖
hljs.initHighlightingOnLoad()(v11+ 已废弃),改用hljs.highlightElement(codeEl)对单个元素手动高亮 - 每次插入新代码块后,先补全语义属性:
codeEl.setAttribute('class', 'language-js')、preEl.setAttribute('role', 'region')、preEl.setAttribute('aria-label', 'JS 示例'),再调用高亮 - 避免 CMS 自动把
<pre class="brush:php;toolbar:false;"><code></code> 包进 <code><p></p></code> 或删掉换行——这些破坏结构的操作会让 <code>highlight.js</code> 完全忽略该块;可在渲染前用 <code>script type="text/plain"</code> 存原始代码,JS 再提取插入,保真度最高</pre> - 服务端渲染(SSR)场景下,务必在 HTML 输出时就写对
<pre class="brush:php;toolbar:false;"><code class="html"></code> 结构,不要留到客户端补,否则首屏对辅助技术就是不可读的</pre>
高亮后的 span 标签怎么避免朗读噪音
highlight.js 会在 <code> 里插入大量 <span class="hljs-keyword"></span> 等标签来着色,但默认情况下,屏幕阅读器会把这些 span 当成独立文本节点,导致“function space const space name space = space”式碎读,完全无法理解语义。
动态插入的代码块怎么保证无障碍生效
博客系统、CMS 或 Markdown 渲染器常在 DOM 加载后才把代码块塞进页面,这时 hljs.highlightAll() 可能已执行完毕,新代码块不会被处理;更麻烦的是,如果渲染过程把 <pre class="brush:php;toolbar:false;"><code></code> 拆开或加了多余 wrapper,高亮和无障碍属性都会失效。</pre>
最易被忽略的一点:代码块的 aria-label 不能写死成“代码示例”,必须体现语言和用途,比如 aria-label="Python 函数定义:计算斐波那契数列"。否则用户听到十段“代码示例”,根本分不清哪段该用、哪段是错的。这不是细节,是决定他们愿不愿意继续听下去的关键。











