html link作法
HTML 連結作法:<a> vs <button>
TL;DR連結用 <a href>、行為用 <button>,永遠不要把它們互相包。語意正確 = 鍵盤可達 + 螢幕閱讀器友善 + SEO 給分。
四種寫法比較(三種是錯的)
<!-- ✅ 正確:用 <a> -->
<a href="https://example.com">Link</a>
<!-- ⚠️ 不推薦:<button> 模擬連結 -->
<button onclick="location.href='https://example.com'">Link</button>
<!-- ❌ 錯誤:<a> 包 <button> -->
<a href="https://example.com"><button>Link</button></a>
<!-- ❌ 錯誤:<button> 包 <a>(在 HTML 規範上是 invalid) -->
<button><a href="https://example.com">Link</a></button>
| 方式 | 鍵盤 | 螢幕閱讀器 | SEO | 中鍵新分頁 | Cmd+Click | 推薦 |
|---|---|---|---|---|---|---|
<a href> | ✅ Tab + Enter | 「連結」 | ✅ 爬蟲抓得到 | ✅ | ✅ | ⭐ |
<button> + JS | ✅ Tab + Enter/Space | 「按鈕」 | ❌ 看不到 href | ❌ 不行 | ❌ 不行 | ❌ |
<a><button></a> | ⚠️ 雙焦點 | 混亂 | ✅ | ✅ | ✅ | ❌ |
<button><a></button> | ⚠️ HTML 違規 | 混亂 | — | ❌ | ❌ | ❌ |
為什麼不能用 <button> 模擬連結
1. 失去原生連結行為
<a href> 自帶的功能用 <button onclick> 全沒了:
- 中鍵點擊開新分頁
Cmd/Ctrl + Click在背景開新分頁- 右鍵選單(複製連結、新視窗開啟)
- 拖曳到書籤列
2. SEO 爬不到
Google 爬蟲解析 href 屬性,看不到 onclick handler 裡的 URL。把連結藏在 button 裡 = 從搜尋引擎隱形。
3. 螢幕閱讀器念錯角色
VoiceOver / NVDA 念到 <a> 會說「連結 ...」,念到 <button> 會說「按鈕 ...」。==使用者預期不同——按鈕 = 觸發動作、連結 = 跳轉==,弄混會讓視障使用者困惑。
為什麼不能互相包
<a> 包 <button>
雙重 focusable element → Tab 鍵會經過兩次,鍵盤體驗破碎。
<button> 包 <a>
HTML 規範 不允許 <button> 內含互動元素(HTML Spec:「No interactive content descendants」)。瀏覽器會用各種方式「修正」這個錯誤,行為不一致。
想要連結看起來像按鈕?用 CSS
<a href="/signup" class="btn">Sign Up</a>
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background: var(--color-accent);
color: white;
border-radius: 0.5rem;
text-decoration: none;
font-weight: 600;
}
.btn:hover { background: var(--color-accent-hover); }
.btn:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
語意 + 視覺分離 —— 這才是 CSS 的本意。
何時真的該用 <button>
純 前端行為(不跳轉 URL)的時候:
<button type="button" onclick="toggleModal()">Open Modal</button>
<button type="submit">Submit Form</button>
<button type="button" onclick="copyToClipboard()">Copy</button>
判斷準則點擊後 URL 會變嗎?
- 會 →
<a href>- 不會 →
<button>
React / Next.js 注意事項
// ✅ Next.js Link
import Link from 'next/link';
<Link href="/about">About</Link>
// ❌ 用 router.push 在 <button> 裡(失去所有原生連結行為)
<button onClick={() => router.push('/about')}>About</button>
<Link> 內部會 render 成 <a>,所以同樣享有 Cmd+Click、新分頁、SEO 等好處。