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: '第三則公告' }
]
});

即時展示

水平左捲 · 右側暫停按鈕
垂直向上捲 · 左側暫停按鈕

基本選項

選項型別預設值說明
directionstring'right'捲動方向:left、right、up、down
speednumber50捲動速度(px/秒)
itemsarray[]內容項目陣列
gapnumber20項目間距(px)
loopbooleantrue是否循環播放
autoStartbooleantrue是否自動開始
classNamestring''附加至容器的 CSS 類別
containerStylestring|object{}容器行內樣式
pauseOnHoverbooleantrue滑鼠移入時暫停
pauseOnFocusbooleantrue鍵盤聚焦時暫停
keyboardControlbooleantrue啟用鍵盤控制

項目格式

文字

{ 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: '前往範例網站(另開新視窗)' }

樣式選項

選項型別說明
itemStylestring|object全域項目行內樣式,支援 CSS 字串或 JS 物件
itemClassNamestring全域項目 CSS 類別
💡 個別項目的 style / className 會疊加於全域設定之上,不會覆蓋。
// CSS 字串格式
itemStyle: 'color:#fff; font-size:15px; padding:0 8px'

// JS 物件格式
itemStyle: { color: '#fff', fontSize: '15px', padding: '0 8px' }

無障礙選項

選項型別預設值說明
ariaLabelstring'跑馬燈內容'容器的 aria-label
ariaLivestring'polite'aria-live 屬性值
announceItemsbooleantrue是否向螢幕閱讀器播報操作狀態
respectReducedMotionbooleantrue尊重系統 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
}

暫停按鈕選項

選項型別預設值說明
showPauseButtonbooleantrue是否顯示暫停按鈕
pauseButtonPositionstring'bottom-right'按鈕位置(見下方)
pauseButtonLabel.pausestring'暫停跑馬燈'播放中時的 aria-label
pauseButtonLabel.resumestring'繼續跑馬燈'暫停中時的 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-labelaria-pressed背景色
播放中▐▐ 暫停pauseButtonLabel.pausefalse半透明黑
暫停中▶ 播放pauseButtonLabel.resumetrue深綠色

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-root flex 外層容器
  • 並排按鈕改為矩形、高度撐滿、含分隔線,視覺融入 input-group
  • destroy() 支援還原並排 DOM 結構
  • 新增 isInlineMode() 工具函式,統一判斷並排模式
v2.0.6
  • 新增浮動暫停按鈕,符合 WCAG 2.3.3 AAA「充足時間」準則
  • 支援 top/bottom-left/right 四角落位置
  • 圖示動態切換(▐▐ / ▶),aria-pressed 狀態同步
  • 焦點樣式:3px 黃色外框 + 光暈