TadNav v1.5.0
現代化、零依賴、符合 WCAG 2.2 的無障礙導覽選單套件,以純 JavaScript 取代 SmartMenus jQuery。
零依賴
WCAG 2.2
多層巢狀
Hover & Click
hoverClose
Spacer 靠右
CSS 變數主題
MIT License
📁 一、檔案結構
tadnav/
├── tadnav.css ← 套件樣式(CSS 變數主題系統)
├── tadnav.js ← 套件核心邏輯
└── index.html ← 本說明文件 & 互動展示頁
套件本身僅需 tadnav.css 與 tadnav.js 兩個檔案。
Font Awesome 為選用依賴,僅用於箭頭與圖示顯示。
🚀 二、快速開始
步驟 1:引入檔案
<!-- Font Awesome(選用,用於箭頭圖示)-->
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- TadNav 樣式 -->
<link rel="stylesheet" href="tadnav.css">
<!-- TadNav 核心(放在 </body> 前)-->
<script src="tadnav.js"></script>
步驟 2:建立 HTML 結構
<nav class="tadnav-wrapper" aria-label="導覽工具列">
<div class="tadnav-inner">
<!-- 品牌名稱(選用)-->
<div class="tadnav-brand">網站名稱</div>
<!-- 漢堡按鈕(手機版)-->
<button class="tadnav-toggle"
aria-expanded="false"
aria-controls="my-menu"
aria-label="切換選單顯示狀態">
<span class="bar" aria-hidden="true"></span>
<span class="bar" aria-hidden="true"></span>
<span class="bar" aria-hidden="true"></span>
</button>
<!-- 主選單 -->
<ul id="my-menu" class="tadnav-menu" role="menubar" aria-label="主選單">
<!-- 一般連結 -->
<li role="none">
<a href="/home" role="menuitem">首頁</a>
</li>
<!-- 有子選單的項目 -->
<li role="none">
<button class="tadnav-submenu-toggle" role="menuitem"
aria-haspopup="true" aria-expanded="false">
服務 <span class="tadnav-arrow" aria-hidden="true"></span>
</button>
<ul class="tadnav-submenu" role="menu" aria-label="服務">
<li role="none"><a href="/svc1" role="menuitem">服務一</a></li>
<li role="none"><a href="/svc2" role="menuitem">服務二</a></li>
</ul>
</li>
<!-- Spacer:其後項目靠右 -->
<li class="tadnav-spacer" role="none" aria-hidden="true"></li>
<!-- 靠右項目(JS 自動標記,無需手寫 style)-->
<li role="none">
<a href="/login" role="menuitem">登入</a>
</li>
</ul>
</div>
</nav>
步驟 3:初始化
document.addEventListener('DOMContentLoaded', function () {
var nav = new TadNav('#my-menu', {
// 展開方式
trigger: 'hover', // 'hover'(預設)| 'click'
hoverClose: true, // hover 模式下滑走是否收合
// 主題色彩(全部選用,未設定則使用 CSS 預設值)
theme: {
navBg: '#2c3e50',
itemColor: '#ffffff',
itemHoverBg: 'rgba(255,255,255,0.15)',
itemFontSize: '0.95rem',
subBg: '#ffffff',
subItemColor: '#2c3e50',
subItemHoverBg: '#f0f4f8',
subItemFontSize: '0.9rem',
subShadow: '0 4px 16px rgba(0,0,0,0.12)',
innerMaxWidth: '1200px',
}
});
});
🏗️ 三、HTML 結構規則
TadNav 依賴特定的 class 與 ARIA 屬性組合,請嚴格遵守以下規則:
| 元素 | 必要 Class | 必要屬性 | 說明 |
|---|---|---|---|
<nav> | .tadnav-wrapper | aria-label | 最外層容器,CSS 變數寫入此元素 |
<div> | .tadnav-inner | — | flex 排版容器,寬度由 innerMaxWidth 控制 |
<div> | .tadnav-brand | — | 品牌名稱區塊(選用) |
<button> | .tadnav-toggle | aria-expanded aria-controls aria-label | 漢堡按鈕,手機版顯示 |
<ul> | .tadnav-menu | role="menubar" aria-label | 頂層選單根節點,id 需對應 aria-controls |
<li> | — | role="none" | 所有 <li> 皆須加此屬性 |
<a> | — | role="menuitem" | 一般連結項目 |
<button> | .tadnav-submenu-toggle | role="menuitem" aria-haspopup="true" aria-expanded="false" | 子選單觸發按鈕 |
<span> | .tadnav-arrow | aria-hidden="true" | 箭頭指示器(Font Awesome 自動套用) |
<ul> | .tadnav-submenu | role="menu" aria-label | 子選單,必須緊接在 toggle 按鈕之後 |
<li> | .tadnav-spacer | role="none" aria-hidden="true" | 靠右分隔點,其後項目自動靠右展開 |
巢狀子選單:子選單內可再放置
.tadnav-submenu-toggle + .tadnav-submenu,支援無限層巢狀,JS 會自動處理所有 ARIA 屬性。
📌 四、Spacer 自動靠右機制
在頂層 <ul> 中放置一個 .tadnav-spacer,
TadNav 初始化時會自動掃描並將其後所有 <li> 標記 is-right,
子選單自動靠右展開。完全不需手寫任何 style 屬性。
運作原理
<!-- spacer 之前:靠左展開(預設)-->
<li role="none">...</li>
<li role="none">...</li>
<!-- ★ Spacer -->
<li class="tadnav-spacer" role="none" aria-hidden="true"></li>
<!-- spacer 之後:JS 自動加上 is-right,子選單靠右展開 -->
<li role="none">...</li> <!-- → <li class="is-right"> -->
巢狀子選單方向
- 靠左項目的巢狀子選單:向右展開(
left: 100%) - 靠右項目(is-right)的巢狀子選單:向左展開(
right: 100%),避免超出視窗
動態更新:若動態新增或移除選單項目後,呼叫
nav.refresh() 即可重新掃描並更新 is-right 標記。
⚙️ 五、完整 API 參數
var nav = new TadNav('#my-menu', {
// ── 展開觸發 ──────────────────────────────
trigger: 'hover', // 'hover'(預設)| 'click'
hoverClose: true, // hover 模式:滑走是否收合(預設 true)
hoverDelay: 200, // 滑入後延遲展開 ms
hideDelay: 300, // 滑出後延遲收合 ms(hoverClose:true 時有效)
// ── 響應式 ────────────────────────────────
breakpoint: 768, // 切換漢堡選單的視窗寬度斷點 px
// ── 行為 ──────────────────────────────────
collisionDetection: true, // 子選單超出視窗時自動翻轉
closeOnOutsideClick: true, // 點擊外部關閉
closeOnEsc: true, // Esc 鍵關閉
// ── 主題色彩(見第六節)──────────────────
theme: { ... },
// ── 事件回呼 ──────────────────────────────
onInit: null, // (instance) => {}
onOpen: null, // (submenu, triggerEl) => {}
onClose: null, // (submenu, triggerEl) => {}
onBreakpointChange: null, // (isMobile) => {}
onDestroy: null, // () => {}
});
| 參數 | 類型 | 預設值 | 說明 |
|---|---|---|---|
trigger | string | 'hover' | 展開觸發方式:'hover' 或 'click' |
hoverClose | boolean | true | hover 模式下,滑鼠移出是否自動收合 |
hoverDelay | number | 200 | 滑鼠移入後延遲展開毫秒數 |
hideDelay | number | 300 | 滑鼠移出後延遲收合毫秒數 |
breakpoint | number | 768 | 視窗寬度 ≤ 此值時切換為漢堡模式 |
collisionDetection | boolean | true | 子選單超出視窗邊界時自動翻轉方向 |
closeOnOutsideClick | boolean | true | 點擊選單外部時關閉所有子選單 |
closeOnEsc | boolean | true | 按 Esc 鍵時關閉所有子選單 |
theme | Object | {} | 主題色彩物件,見第六節 |
onInit | Function | null | 初始化完成後觸發,參數:(instance) |
onOpen | Function | null | 子選單展開時觸發,參數:(submenu, triggerEl) |
onClose | Function | null | 子選單收合時觸發,參數:(submenu, triggerEl) |
onBreakpointChange | Function | null | 桌機 ↔ 手機切換時觸發,參數:(isMobile) |
onDestroy | Function | null | destroy() 完成後觸發 |
🎨 六、主題色彩系統
TadNav 使用 CSS 自訂屬性(Custom Properties)管理所有顏色與尺寸。
tadnav.css 的 :root 定義預設值;
JS 初始化時若傳入 theme 物件,會將對應值寫入
.tadnav-wrapper 的 inline style,
覆寫預設值,支援同頁多個導覽列各自獨立主題。
theme 參數完整列表
📐 版面
| theme 屬性 | CSS 變數 | 預設值 | 說明 |
|---|---|---|---|
innerMaxWidth v1.5 | --tadnav-inner-max-width | 100% | 導覽列內容最大寬度,設 '1200px' 可置中限寬 |
🟦 導覽列
| theme 屬性 | CSS 變數 | 預設值 | 說明 |
|---|---|---|---|
navBg | --tadnav-nav-bg | 藍色漸層 | 導覽列背景,支援純色、漸層、圖片 |
navShadow | --tadnav-nav-shadow | 淡陰影 | 導覽列底部陰影 |
brandColor | --tadnav-brand-color | #ffffff | 品牌文字顏色 |
🔤 頂層項目
| theme 屬性 | CSS 變數 | 預設值 | 說明 |
|---|---|---|---|
itemColor | --tadnav-item-color | #ffffff | 頂層項目文字顏色 |
itemBg | --tadnav-item-bg | transparent | 頂層項目背景 |
itemFontSize v1.5 | --tadnav-item-font-size | 0.9rem | 頂層項目文字大小 |
itemHoverBg | --tadnav-item-hover-bg | 白色半透明 | 頂層項目 Hover / Active 背景 |
itemHoverColor | --tadnav-item-hover-color | #ffffff | 頂層項目 Hover / Active 文字顏色 |
itemAccent | --tadnav-item-accent | 白色半透明 | 頂層項目 Hover 底線強調色 |
📋 子選單面板
| theme 屬性 | CSS 變數 | 預設值 | 說明 |
|---|---|---|---|
subBg | --tadnav-sub-bg | #ffffff | 子選單面板背景 |
subShadow v1.5 | --tadnav-sub-shadow | 淡陰影 | 子選單陰影,傳入 'none' 可完全關閉 |
subBorder | --tadnav-sub-border | #2e86c1 | 子選單頂部 / 側邊強調線顏色 |
subDivider | --tadnav-sub-divider | #f0f4f8 | 子選單項目間分隔線顏色 |
🔤 子選單項目
| theme 屬性 | CSS 變數 | 預設值 | 說明 |
|---|---|---|---|
subItemColor | --tadnav-sub-item-color | #1a3a5c | 子選單項目文字顏色 |
subItemBg | --tadnav-sub-item-bg | transparent | 子選單項目背景 |
subItemFontSize v1.5 | --tadnav-sub-item-font-size | 0.88rem | 子選單項目文字大小 |
subItemHoverBg | --tadnav-sub-item-hover-bg | #eaf4fd | 子選單項目 Hover 背景 |
subItemHoverColor | --tadnav-sub-item-hover-color | #1a5276 | 子選單項目 Hover 文字顏色 |
📱 漢堡按鈕 & 手機版
| theme 屬性 | CSS 變數 | 預設值 | 說明 |
|---|---|---|---|
toggleColor | --tadnav-toggle-color | #ffffff | 漢堡按鈕線條顏色 |
toggleHoverBg | --tadnav-toggle-hover-bg | 白色半透明 | 漢堡按鈕 Hover 背景 |
mobileSubBg | --tadnav-mobile-sub-bg | 黑色半透明 | 手機版子選單背景 |
mobileSubBorder | --tadnav-mobile-sub-border | 白色半透明 | 手機版子選單左側邊框 |
mobileSubColor | --tadnav-mobile-sub-color | 白色半透明 | 手機版子選單文字顏色 |
mobileItemBorder | --tadnav-mobile-item-border | 白色半透明 | 手機版項目分隔線顏色 |
動態更換主題
// 部分更新(合併)
nav.setTheme({
navBg: '#2c3e50',
itemColor: '#ecf0f1',
});
// 完全取代(先清除所有舊設定)
nav.setTheme({
navBg: '#ffffff',
itemColor: '#333333',
itemHoverBg: '#f0f0f0',
itemHoverColor: '#000000',
}, false);
// 重設回 CSS 預設值
nav.resetTheme();
// 取得目前主題
var current = nav.getTheme();
console.log(current);
🖌️ 七、主題互動展示
以下控制項可即時調整上方導覽列的外觀,所有變更都透過 nav.setTheme() 套用。
預設主題
導覽列背景與文字
頂層項目
子選單面板
子選單項目
版面寬度
🖱️ 八、trigger 模式
控制子選單的展開觸發方式,可在初始化時設定,也可執行期間動態切換。
| 模式 | 展開方式 | 適用情境 |
|---|---|---|
'hover'(預設) | 滑鼠移入後延遲展開 | 一般網站導覽、桌機為主 |
'click' | 點擊 toggle 按鈕展開 | 應用程式介面、觸控裝置、無障礙優先 |
// 初始化時設定
var nav = new TadNav('#my-menu', { trigger: 'click' });
// 執行期間動態切換
nav.setTrigger('click');
nav.setTrigger('hover');
🔒 九、hoverClose 控制
hoverClose 控制 hover 模式下,滑鼠離開後是否自動收合子選單。
在 trigger: 'click' 模式下此參數無作用。
| 值 | 行為 | 適用情境 |
|---|---|---|
true(預設) | 滑走後自動收合 | 一般網站導覽 |
false | 滑走不收合,需點擊外部或按 Esc 關閉 | 複雜多層選單、需仔細閱讀選項時 |
// 滑入展開,滑走不收合
var nav = new TadNav('#my-menu', {
trigger: 'hover',
hoverClose: false
});
// 執行期間動態切換
nav.setHoverClose(false); // 關閉自動收合
nav.setHoverClose(true); // 恢復自動收合
注意:使用
hoverClose: false 時,請確保
closeOnOutsideClick: true(預設已開啟),
否則使用者將難以關閉展開的子選單。
🔧 十、公開方法
| 方法 | 參數 | 說明 |
|---|---|---|
open(submenu) | HTMLElement | 手動展開指定子選單 |
close(submenu) | HTMLElement | 手動收合指 |