TadMarquee
符合 WCAG 2.1 / 2.3 AAA 無障礙標準的跑馬燈套件
📦 v2.0.7
♿ WCAG AAA
⌨️ 鍵盤友善
📱 響應式
🔁 無縫循環
⏸ 暫停按鈕
簡介
TadMarquee 是一套純 JavaScript 跑馬燈套件,無需任何外部依賴,支援四方向捲動、無縫循環播放,並完整符合 WCAG AAA 無障礙標準。
| 特色 | 說明 |
|---|---|
| 四方向捲動 | left、right、up、down |
| 無縫循環 | 第 N 則後自動接回第 1 則,不跳動 |
| 暫停按鈕 | 6 種位置,含 input-group 並排模式 |
| 鍵盤導航 | 方向鍵、Home/End、Space/Enter、Escape |
| 螢幕閱讀器 | ARIA 標籤、即時播報、複本隱藏 |
| 響應式速度 | 依螢幕寬度自動調整捲動速度 |
| reduced-motion | 尊重系統動畫偏好設定 |
快速開始
引入檔案
<!-- CSS -->
<link rel="stylesheet" href="tad_marquee.css">
<!-- JS -->
<script src="tad_marquee.js"></script>
HTML 容器
<div id="my-marquee" style="width:100%; height:44px;"></div>
初始化
const marquee = new TadMarquee('my-marquee', {
direction: 'left',
speed: 60,
items: [
{ type: 'text', content: '第一則公告' },
{ type: 'text', content: '第二則公告' },
{ type: 'text', content: '第三則公告' }
]
});
即時展示
水平左捲 · 右側暫停按鈕
垂直向上捲 · 左側暫停按鈕
基本選項
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
direction | string | 'right' | 捲動方向:left、right、up、down |
speed | number | 50 | 捲動速度(px/秒) |
items | array | [] | 內容項目陣列 |
gap | number | 20 | 項目間距(px) |
loop | boolean | true | 是否循環播放 |
autoStart | boolean | true | 是否自動開始 |
className | string | '' | 附加至容器的 CSS 類別 |
containerStyle | string|object | {} | 容器行內樣式 |
pauseOnHover | boolean | true | 滑鼠移入時暫停 |
pauseOnFocus | boolean | true | 鍵盤聚焦時暫停 |
keyboardControl | boolean | true | 啟用鍵盤控制 |
項目格式
文字
{ type: 'text', content: '公告內容',
style: 'color:red; font-weight:bold', // 可選
className: 'my-class' } // 可選
圖片
{ type: 'image', content: 'https://example.com/img.png',
alt: '圖片說明', text: '附加文字' }
HTML
{ type: 'html', content: '<strong>重要</strong> 公告' }
含連結(任何類型皆可加)
{ type: 'text', content: '點我前往',
link: 'https://example.com',
target: '_blank',
rel: 'noopener noreferrer',
ariaLabel: '前往範例網站(另開新視窗)' }
樣式選項
| 選項 | 型別 | 說明 |
|---|---|---|
itemStyle | string|object | 全域項目行內樣式,支援 CSS 字串或 JS 物件 |
itemClassName | string | 全域項目 CSS 類別 |
💡 個別項目的
style / className 會疊加於全域設定之上,不會覆蓋。
// CSS 字串格式
itemStyle: 'color:#fff; font-size:15px; padding:0 8px'
// JS 物件格式
itemStyle: { color: '#fff', fontSize: '15px', padding: '0 8px' }
無障礙選項
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
ariaLabel | string | '跑馬燈內容' | 容器的 aria-label |
ariaLive | string | 'polite' | aria-live 屬性值 |
announceItems | boolean | true | 是否向螢幕閱讀器播報操作狀態 |
respectReducedMotion | boolean | true | 尊重系統 prefers-reduced-motion |
響應式選項
| 斷點 | 螢幕寬度 | 速度倍率 |
|---|---|---|
mobile | < 480px | × 0.6 |
tablet | < 768px | × 0.8 |
desktop | < 1024px | × 1.0 |
large | ≥ 1024px | × 1.2 |
responsiveBreakpoints: {
mobile: 480,
tablet: 768,
desktop: 1024
}
暫停按鈕選項
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
showPauseButton | boolean | true | 是否顯示暫停按鈕 |
pauseButtonPosition | string | 'bottom-right' | 按鈕位置(見下方) |
pauseButtonLabel.pause | string | '暫停跑馬燈' | 播放中時的 aria-label |
pauseButtonLabel.resume | string | '繼續跑馬燈' | 暫停中時的 aria-label |
位置設定
角落模式(浮動圓形按鈕)
按鈕以 position: absolute 疊加在跑馬燈內部四個角落,不影響容器尺寸。
top-left / top-right
┌─▐▐──────────────────▐▐─┐
│ 跑馬燈內容捲動中... │
│ │
└────────────────────────────┘
│ 跑馬燈內容捲動中... │
│ │
└────────────────────────────┘
bottom-left / bottom-right
┌────────────────────────────┐
│ 跑馬燈內容捲動中... │
│ │
└─▐▐──────────────────▐▐─┘
│ 跑馬燈內容捲動中... │
│ │
└─▐▐──────────────────▐▐─┘
並排模式(input-group 矩形按鈕)
在容器外自動插入 .tad-marquee-root flex 外層,按鈕與跑馬燈並排,高度自動撐滿。
left
┌──────┬──────────────────────┐
│▐▐ │跑馬燈內容捲動中... │
└──────┴──────────────────────┘
│▐▐ │跑馬燈內容捲動中... │
└──────┴──────────────────────┘
right
┌──────────────────────┬──────┐
│跑馬燈內容捲動中... │ ▐▐│
└──────────────────────┴──────┘
│跑馬燈內容捲動中... │ ▐▐│
└──────────────────────┴──────┘
⚠️ 並排模式會在原始容器外自動插入
.tad-marquee-root,呼叫 destroy() 時會自動還原 DOM 結構。
圖示切換邏輯
| 狀態 | 圖示 | aria-label | aria-pressed | 背景色 |
|---|---|---|---|---|
| 播放中 | ▐▐ 暫停 | pauseButtonLabel.pause | false | 半透明黑 |
| 暫停中 | ▶ 播放 | pauseButtonLabel.resume | true | 深綠色 |
API:播放控制
所有方法皆回傳 this,支援鏈式呼叫。
marquee.start() // 開始播放
marquee.stop() // 停止播放並重置狀態
marquee.pause() // 暫停(保持目前位置)
marquee.resume() // 從暫停處繼續
API:內容更新
// 更換所有項目
marquee.updateItems([
{ type: 'text', content: '新公告一' },
{ type: 'text', content: '新公告二' }
]);
// 修改捲動速度
marquee.setSpeed(80);
// 修改捲動方向(會重建 DOM 結構)
marquee.changeDirection('up');
API:樣式更新
// 合併更新(預設)
marquee.setItemStyle('color:red; font-size:18px');
marquee.setItemStyle({ color: 'red' });
// 完全取代
marquee.setItemStyle({ color: 'blue' }, false);
// 更換 CSS 類別
marquee.setItemClassName('new-class', 'old-class');
API:查詢狀態
// 取得所有設定
const opts = marquee.getOptions();
// 取得執行狀態
const status = marquee.getStatus();
// 回傳:{
// isRunning, isPaused, isPausedByUser,
// direction, speed, itemCount, isSingleItem,
// prefersReducedMotion, currentBreakpoint,
// loopSize, inlineMode
// }
// 銷毀並還原 DOM
marquee.destroy();
回呼函式
new TadMarquee('my-marquee', {
onStart: () => console.log('開始播放'),
onStop: () => console.log('停止播放'),
onPause: () => console.log('已暫停'),
onResume: () => console.log('已繼續'),
onDirectionChange: (dir) => console.log('方向:', dir),
onBreakpointChange: (bp) => console.log('斷點:', bp)
});
鍵盤操作
| 按鍵 | 功能 |
|---|---|
| Tab | 將焦點移至暫停按鈕或內部連結 |
| Space / Enter | 切換暫停/繼續(焦點在容器上時) |
| → / ↓ | 聚焦下一個項目 |
| ← / ↑ | 聚焦上一個項目 |
| Home | 聚焦第一個項目 |
| End | 聚焦最後一個項目 |
| Escape | 移除焦點,離開跑馬燈 |
💡 焦點移入暫停按鈕時,Enter/Space 由瀏覽器原生行為觸發按鈕點擊,不會與容器層的鍵盤事件衝突。
無障礙設計說明
| WCAG 準則 | 等級 | 實作方式 |
|---|---|---|
| 1.3.1 資訊與關係 | A | role="region"、role="list"、role="listitem"、aria-label |
| 1.4.3 色彩對比 | AA | 暫停按鈕白色圖示對半透明黑底,對比度 > 4.5:1 |
| 2.1.1 鍵盤 | A | 所有功能均可透過鍵盤操作,無滑鼠陷阱 |
| 2.2.2 暫停、停止、隱藏 | A | 提供可見暫停按鈕,使用者可隨時暫停捲動 |
| 2.3.3 動畫(三閃或低於閾值) | AAA | 尊重 prefers-reduced-motion,偵測到時自動暫停 |
| 2.4.11 焦點外觀 | AA | 焦點時顯示 3px 黃色外框 + 光暈效果 |
| 2.5.5 目標尺寸 | AAA | 並排按鈕 40px 寬、角落按鈕 32px,觸控友善 |
| 4.1.2 名稱、角色、值 | A | aria-pressed、aria-label 動態更新反映按鈕狀態 |
螢幕閱讀器行為
- 複本項目(無縫循環用)統一設定
aria-hidden="true"及tabindex="-1" - 鍵盤導航僅能聚焦原始組的項目,不會重複播報
- 操作(暫停、繼續、方向變更)透過
role="status"的隱藏元素即時播報
DOM 結構說明
角落模式
#my-marquee.tad-marquee-container ← 原始容器(position: relative)
├── div.tad-marquee-wrapper ← 捲動內容(position: absolute)
│ ├── div.tad-marquee-item ← 原始項目(可被 AT 讀取)
│ ├── div.tad-marquee-gap
│ ├── div.tad-marquee-item
│ ├── div.tad-marquee-gap
│ ├── div.tad-marquee-item aria-hidden="true" ← 複本
│ └── ...
└── button.tad-marquee-pause-btn ← 浮動圓形按鈕(position: absolute)
並排模式(left / right)
div.tad-marquee-root.tad-marquee-inline ← 自動插入外層(display: flex)
├── button.tad-marquee-pause-btn ← 左側按鈕(position: left 時)
├── #my-marquee.tad-marquee-container ← 原始容器(flex: 1)
│ └── div.tad-marquee-wrapper
│ └── ...(同上)
└── button.tad-marquee-pause-btn ← 右側按鈕(position: right 時)
完整範例
範例一:水平左捲 + 右側暫停按鈕
<div id="marquee-h" style="height:44px; background:#1a1a2e;"></div>
<script>
new TadMarquee('marquee-h', {
direction: 'left',
speed: 60,
pauseButtonPosition: 'right',
itemStyle: 'color:#fff; font-size:15px; padding:0 8px',
items: [
{ type: 'text', content: '📢 系統將於今晚 23:00 進行維護' },
{ type: 'text', content: '🎉 新功能上線:支援深色模式' },
{ type: 'text', content: '📌 請更新您的密碼以確保帳號安全' },
{ type: 'text', content: '🔔 活動報名截止日期:2026/03/31' },
{ type: 'text', content: '✅ 服務恢復正常,感謝您的耐心等候' }
]
});
</script>
範例二:垂直向上捲 + 左側暫停按鈕
<div id="marquee-v" style="height:120px; background:#0f3460;"></div>
<script>
new TadMarquee('marquee-v', {
direction: 'up',
speed: 40,
gap: 16,
pauseButtonPosition: 'left',
itemStyle: { color: '#e0e0e0', fontSize: '14px', padding: '6px 12px' },
items: [
{ type: 'text', content: '第一則:超出容器寬度時會自動換行顯示' },
{ type: 'text', content: '第二則:系統更新通知' },
{ type: 'text', content: '第三則:活動資訊' },
{ type: 'text', content: '第四則:重要提醒事項' },
{ type: 'text', content: '第五則:聯絡客服請撥 0800-000-000' }
]
});
</script>
範例三:含連結 + 角落暫停按鈕 + 回呼
new TadMarquee('marquee-link', {
direction: 'left',
pauseButtonPosition: 'bottom-right',
pauseButtonLabel: { pause: 'Pause', resume: 'Play' },
items: [
{ type: 'text', content: '🔗 查看最新公告',
link: 'https://example.com',
ariaLabel: '查看最新公告(另開新視窗)' },
{ type: 'html',
content: '<span style="color:#ff6b6b">⚠️ 緊急通知</span>:請立即更新系統' }
],
onPause: () => console.log('使用者暫停'),
onResume: () => console.log('使用者繼續')
});
範例四:動態更新內容
const marquee = new TadMarquee('my-marquee', {
direction: 'left',
items: [{ type: 'text', content: '初始內容' }]
});
// 3 秒後更換內容
setTimeout(() => {
marquee.updateItems([
{ type: 'text', content: '更新後的第一則' },
{ type: 'text', content: '更新後的第二則' }
]);
}, 3000);
marquee.setSpeed(100);
console.log(marquee.getStatus());
版本異動記錄
v2.0.7
pauseButtonPosition新增'left'、'right'並排模式- 並排模式自動插入
.tad-marquee-rootflex 外層容器 - 並排按鈕改為矩形、高度撐滿、含分隔線,視覺融入 input-group
destroy()支援還原並排 DOM 結構- 新增
isInlineMode()工具函式,統一判斷並排模式
v2.0.6
- 新增浮動暫停按鈕,符合 WCAG 2.3.3 AAA「充足時間」準則
- 支援
top/bottom-left/right四角落位置 - 圖示動態切換(▐▐ / ▶),
aria-pressed狀態同步 - 焦點樣式:3px 黃色外框 + 光暈