TreeInterceptor(網頁虛擬樹狀圖) 裡去找答案就行了!」當我們在 Google Chrome 瀏覽網頁時,按下 NVDA+Shift+C,外掛會立刻自動掃描整個網頁,用語音和點字告訴你:「這個網頁總共有幾個核取方塊」。
要在網頁中順暢導航,我們需要懂得將 MDN 的 Web ARIA 標準與 NVDA 的底層虛擬緩衝區相結合。
NVDA+Shift+C。treeInterceptor (VirtualBuffer),且該虛擬緩衝區必須是活著的 (isAlive)。如果沒有,代表使用者不在網頁上,必須結束並提示。<input type="checkbox"> 或帶有 role="checkbox" 的 ARIA 標籤,在 NVDA 底層都會被翻譯成 controlTypes.Role.CHECKBOX。這就是我們要找的身分證!treeInterceptor.rootNVDAObject 進行樹狀搜尋,或者使用 textInfos 進行快速查找),去計算符合條件的物件數量。請複製以下 Prompt 貼給你的 AI:
「你現在是深諳 NVDA 官方架構與 Web ARIA 無障礙規範的頂尖工程師。請幫我寫一個全域外掛(GlobalPlugin),檔案名為
webScanner.py。1. 開發需求描述: - 功能目標:按下快捷鍵
kb:NVDA+shift+c時,掃描當前網頁中有幾個『核取方塊』並朗讀數量。 - 環境判定:請先使用api.getFocusObject()獲取當前焦點。接著,請檢查該焦點是否具有treeInterceptor屬性。如果沒有,表示不在網頁瀏覽模式,請呼叫ui.message("請在網頁瀏覽模式下使用此功能")並結束。 - 搜尋邏輯:當確認在網頁虛擬樹中後,請遍歷treeInterceptor.rootNVDAObject及其所有子節點(使用遞迴搜尋或樹狀迭代器)。只要節點的role等於controlTypes.Role.CHECKBOX,就將計數器加一。 - 播報提示:最後使用ui.message()朗讀出『此網頁共有 X 個核取方塊』。2. 規範限制: - 程式碼必須結構清晰,且必須包含給一般人看得懂的詳細中文註釋,解釋每一行程式碼的運作原理與遍歷邏輯,讓不懂程式碼的人也能輕鬆讀懂。
請直接輸出完整程式碼。」
AI 產出的 webScanner.py 結構如下,我們一起來看看它是怎麼在網頁虛擬樹裡「抓鬼」的:
# 引入全域外掛基礎類別
import globalPluginHandler
# 引入快捷鍵綁定裝飾器
import scriptHandler
# 引入 NVDA 核心 API
import api
# 引入控制控制項類型的定義(Role)
import controlTypes
# 引入語音/點字播報模組
import ui
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
# 綁定快捷鍵 NVDA+Shift+C
@scriptHandler.script(
description="Scans the web page for checkboxes",
gestures=["kb:NVDA+shift+c"]
)
def script_scanCheckboxes(self, gesture):
# 1. 抓取目前使用者的焦點物件
focusObj = api.getFocusObject()
# 2. 檢查這個焦點物件身上,有沒有「網頁虛擬樹(treeInterceptor)」這個構造
# 這是判斷我們是不是在瀏覽網頁、看 PDF 的關鍵
vbuf = getattr(focusObj, 'treeInterceptor', None)
# 如果找不到,或是這個虛擬樹已經失效了
if not vbuf or not vbuf.isAlive:
# 朗讀警告訊息,並結束外掛
ui.message("請在網頁瀏覽模式下使用此功能")
return
# 3. 抓取整張網頁的最頂端根節點
root = vbuf.rootNVDAObject
# 4. 呼叫我們自己寫的樹狀搜尋工具,傳入根節點開始數數
count = self._count_checkboxes(root)
# 5. 將最終數出來的數量報讀給使用者聽
ui.message(f"此網頁共有 {count} 個核取方塊")
# 這是一個遞迴輔助函數,用來深入每一個分支節點數數
def _count_checkboxes(self, obj):
count = 0
# 檢查當前節點的角色是不是「核取方塊」
if obj.role == controlTypes.Role.CHECKBOX:
count += 1
# 接著,把這個節點底下所有的子節點(children)通通拉出來,重複執行一樣的檢查
for child in obj.children:
count += self._count_checkboxes(child)
return count
getattr(focusObj, 'treeInterceptor', None):這是無障礙開發的專屬技巧。它會去檢查這個視窗有沒有被 NVDA 翻譯成「虛擬網頁樹」。_count_checkboxes(遞迴搜尋):它會像樹枝發芽一樣,一層層往下摸,直到把網頁裡所有的 CHECKBOX 按鈕都數完為止!webScanner.py 放進 globalPlugins 並重啟。NVDA+Shift+C,聽取語音報出的數量。大功告成!👉 下一堂課:Lecture 18 【大師專案】商用級隱私防護網:語音過濾器與 GUI 設定面板
👉 回課程大綱:學習地圖
以下題目專為尋求真正硬核觀念與專家級實戰能力的學員設計。題目無法從講義表面抄寫解答,請嘗試獨立思考後再點擊展開解析。
【實戰情境問題】:當 NVDA 使用者在 Chrome 中閱讀網頁時,按 `NVDA + Space` 可以切換「瀏覽模式 (Browse Mode)」與「焦點模式 (Focus Mode)」。這背後的 `TreeInterceptor` 如何在背景建立網頁的虛擬 DOM 緩衝區?
【鑑別點說明】:測試學員對 NVDA 瀏覽模式與網頁互動底層 Virtual Buffer 運作原理的理解。
【專家級解答與深度剖析】: 在「焦點模式」下,鍵盤按鍵會直接傳送給瀏覽器(如在輸入框打字);而「瀏覽模式」下,NVDA 的 `TreeInterceptor` 會透過 IAccessible2 / UIA 介面,將網頁的 DOM 樹抽取出文字與結構,在 NVDA 記憶體中建立一份「虛擬文件緩衝區 (Virtual Buffer)」。此時使用者的上下方向鍵與單鍵導航(如 H 鍵)會被 `TreeInterceptor` 攔截,用來在虛擬緩衝區中移動審閱游標並播報,從而解鎖了極速網頁導航的能力。
【實戰情境問題】:在開發無障礙 Web 應用時,如果將一個每 3 秒更新一次的聊天訊息設定為 `aria-live="assertive"`,會對視障學員造成什麼毀滅性的閱讀體驗?
【鑑別點說明】:深剖 WAI-ARIA 動態區域 (Live Regions) 在螢幕報讀器 Speech Queue 中的搶占優先級。
【專家級解答與深度剖析】: `aria-live="assertive"` 的優先級是最高搶占式(Preemptive)。一旦有新內容更新,NVDA 會**立刻中斷目前正在朗讀的任何句子**並強制播報新訊息。如果更新頻率高(如每 3 秒),學員試圖閱讀講義文章時,話講到一半就會被強行打斷,永遠無法讀完一段完整的句子。正解:非緊急訊息必須使用 `aria-live="polite"`,它會靜靜等待 NVDA 講完當前句子後,才禮貌地播報新動態。