🎙️ Lecture 19: 【實戰演練六】Browse Mode 網頁快鍵導航 (clickable_nav)

NOTE作者資訊 (Author Info)

🎧 錄音室開場:解放網頁導航的全新快捷鍵!


🎯 專案解密:clickable_nav 的核心運作原理

如果我們打開這個外掛的原始碼(clickable_nav/__init__.py),我們會看到幾個高階大師技巧。我們不改代碼,但我們要學會「看懂」它為什麼這樣寫!

1. 確保只在「網頁模式」下生效

網頁跳轉只有在瀏覽器中才有意義。代碼中有一段安全判斷:

vbuf = getattr(api.getFocusObject(), 'treeInterceptor', None)
if not vbuf or not vbuf.isAlive:
    return gesture.send()

這段話的意思是:「如果你現在不在網頁虛擬緩衝區 (TreeInterceptor),那這個快捷鍵 Z 就不歸我管,請把按鍵 send() 還給 Windows,讓使用者可以正常打出英文字母 Z。」這就是我們說過的安全邊界

2. 多重特徵搜尋(找尋目標)

怎麼在茫茫網頁中找到「可以點擊」的東西? 外掛寫了三個搜尋條件 (search_options): 1. states 包含 CLICKABLE:NVDA 認定的可點擊狀態。 2. clickable 屬性為 True。 3. IAccessibleAction_click:這是底層的無障礙點擊行為。 AI 透過這三個條件,呼叫 vbuf._iterNodesByAttribs,瞬間在虛擬樹中搜出下一個目標,並將游標移過去 (item.moveTo())!


📝 大導演的 Vibe Coding 派工單 (Prompt)

如果你想要 AI 幫你重頭寫出這個外掛,你的 Prompt 該怎麼下?

「你現在是頂尖的 NVDA 外掛工程師。請幫我寫一個全域外掛,實現網頁中的『可點擊元素單鍵導航』。

開發規範: 1. 請使用 @scriptHandler.script 綁定快捷鍵 Z (下一個) 與 Shift+Z (上一個)。 2. 【安全檢查】:執行前,先獲取 api.getFocusObject().treeInterceptor。如果不在 Browse Mode,請使用 gesture.send() 將按鍵放行給系統。 3. 【元素搜尋】:請使用 vbuf._iterNodesByAttribs 向下或向上搜尋。搜尋條件必須涵蓋 controlTypes.State.CLICKABLE 狀態、clickable 屬性,以及具備 IAccessibleAction_click 動作的節點。 4. 找到後,請呼叫節點的 moveTo()report() 來移動焦點並朗讀。找不到則用語音提示。

請給我完整的 __init__.py 程式碼。」


🎓 學習總結

透過閱讀 clickable_nav 的代碼,我們學到了非常實用的技巧:在寫全域外掛時,如果你的快捷鍵是普通字母(如 Z),請務必在判斷條件不符時,呼叫 gesture.send() 放行按鍵。否則你的 NVDA 會把你鍵盤上的 Z 鍵永遠吃掉,讓你再也打不出 Z!

👉 下一堂課:Lecture 20 【實戰演練七】進階語音過濾與正則運算 (brlReader)
👉 回課程大綱:學習地圖

🔥 專家級深度思考與實戰挑戰 (Expert Challenge)

以下題目專為尋求真正硬核觀念與專家級實戰能力的學員設計。題目無法從講義表面抄寫解答,請嘗試獨立思考後再點擊展開解析。

❓ 挑戰 1:網頁「假按鈕 (`
`)」對 NVDA 的無障礙殺傷力 👉 點擊展開專家解析答案

【實戰情境問題】:許多前端開發者喜歡用 `

` 來製作按鈕。為什麼這對 NVDA 使用者是嚴重的障礙?如何透過 HTML/ARIA 進行標準化修復?

【鑑別點說明】:深剖 HTML 語意化 (Semantic HTML) 與鍵盤可存取性 (Keyboard Accessibility)。

【專家級解答與深度剖析】: `

` 在無障礙樹中角色為 `section` 或 `generic`,NVDA 讀到它時不會提示「按鈕」,視障使用者不知道它是可點擊元件。此外,`
` 預設無法接收鍵盤焦點(無法用 Tab 鍵切換到位),且按 Enter 或 Space 鍵不會觸發 `onclick` 事件。標準修復:1. 直接改用語意化 `
❓ 挑戰 2:NVDA 可點擊元件導航外掛的實體點擊模擬 (Click Simulation) 👉 點擊展開專家解析答案

【實戰情境問題】:當 NVDA 外掛找到一個可點擊元件並嘗試對其觸發點擊時,`obj.doDefaultAction()` 與 `win32api.SetCursorPos() + mouse_event()` 有何本質差異?

【鑑別點說明】:測試學員對無障礙程式化動作 (API Action) 與物理滑鼠模擬 (Hardware Mouse Event) 的差異理解。

【專家級解答與深度剖析】: `obj.doDefaultAction()` 是呼叫無障礙 API 的程式化動作(如發送 UIA Invoke Pattern),不需要移動實體滑鼠指標,速度極快且不影響使用者螢幕游標。但如果目標網頁是用極度不規範的 JavaScript 監聽物理 `mousedown` 事件,API 動作可能會失效。此時外掛必須降級採用物理模擬:透過 `obj.location` 取得元件的螢幕幾何座標,將滑鼠游標物理移動至該座標中心並發送位元組點擊訊號。