多年來,前端開發與 HTML 之間一直有點尷尬。我們都讀過語意化 HTML,也都知道要使用正確的地標、正確的屬性、可存取的表單,以及有意義的元素。然後我們又回去寫 React。
當整個產業都沉迷於 AI 的同時,瀏覽器卻持續推出讓平台更強大的功能。這些功能讓我們能移除 JS、減少狀態,並直接用 HTML 表達 UI 行為。但因為 AI 的訓練資料來自過去,這些新能力常常被忽略,或是因為舊有模式而不夠被推薦。
過去,若要打開對話框或彈出式視窗,我們通常需要一大段自訂樣板程式碼。
<button id="open-dialog">Open dialog</button>
<dialog id="my-dialog">
<p>Dialog content</p>
<button id="close-dialog">Close</button>
</dialog>
<script>
const dialog = document.getElementById("my-dialog");
document.getElementById("open-dialog").addEventListener("click", () => {
dialog.showModal();
});
document.getElementById("close-dialog").addEventListener("click", () => {
dialog.close();
});
</script>
我們需要一個按鈕來打開對話框、對話框本身,然後再寫 JS 告訴瀏覽器互動邏輯:「當使用者點擊按鈕時,打開對話框」。
如果你用的是 React 或 Vue,這還可能變得更複雜:你需要一些自訂狀態、可能還要把它傳給元件、綁定事件處理器,最後還要確保一切保持同步。
不管是原生寫法還是框架,以上程式碼單看都很合理,任何前端開發者都寫過上百次。但當你寫到第 1000 次之後,就會開始想:我們真的需要應用程式狀態來表示對話框是否開啟嗎?答案是:看情況。有時候確實需要狀態,但有時候我們其實只是在重新打造 HTML 和瀏覽器本來就能直接提供的行為,而到了 2026 年,這些能力已經開箱即用。
Invoker Commands API 提供我們一種以宣告式方式替按鈕指定行為的方法,進而讓我們控制這些互動元素。與其新增事件監聽器,我們可以直接在 HTML 中描述兩者的關係。
有助於我們的屬性是 commandFor 和 command:
commandFor:把按鈕變成「命令觸發器」。它的值是要控制之元素的 ID。command:指定要對該元素執行的動作。<button commandfor="mycoolpopover" command="toggle-popover">
Toggle the popover
</button>
<section id="mycoolpopover" popover>
<button commandfor="mycoolpopover" command="hide-popover">Close</button>
Awesome Popover content
</section>
這些都可以直接移植到你習慣使用的框架中:
export function DeleteButton() {
return (
<>
<button command="show-modal" commandFor="delete-dialog">
Delete account
</button>
<dialog id="delete-dialog">
<h2>Delete account?</h2>
<p>This cannot be undone.</p>
<button command="close" commandFor="delete-dialog">
Cancel
</button>
</dialog>
</>
);
}
這裡最重要的細節是:React 的 JSX 屬性名稱是 commandFor,而最後產生的 HTML 屬性則是 commandfor。
乍看之下,這大概只省下 10 行程式碼而已。這很好,但更有意思的是,責任的轉移:從我們手上,回到瀏覽器。
瀏覽器並不是因為我們寫了更少的 JavaScript 就變得沒那麼強,而是剛好相反。我們終於真的能少寫 JS 了。
不過,在大家開始把對話框都刪掉之前,有個小提醒:Invoker Commands API 是新的。MDN 目前將它列為 Baseline 2025,並表示自 2025 年 12 月起,最新版本的瀏覽器已具備跨瀏覽器支援。舊版瀏覽器可能不支援。
原文出處:https://dev.to/ale3oula/html-is-getting-cool-again-meet-the-invoker-commands-api-1367