純 JavaScript 無縫循環跑馬燈 / Marquee
v1.1.0 ・ 零依賴 ・ WCAG 2 AA以下是一個實際運作的範例,可使用右上角的暫停按鈕或下方按鈕控制:
<link rel="stylesheet" href="tad_loop.css">
<script src="tad_loop.js"></script>
<div id="my-marquee" class="tad-loop">
<div class="item-wrap">
<div class="item"><a href="#">項目 1</a></div>
<div class="item"><a href="#">項目 2</a></div>
<div class="item"><a href="#">項目 3</a></div>
<div class="item"><a href="#">項目 4</a></div>
</div>
</div>
var marquee = new TadLoop('#my-marquee', {
velocity: 2,
forward: true,
pauseOnHover: true,
// WCAG 2 AA 無障礙設定
ariaLabel: '促銷活動跑馬燈',
ariaLabelPause: '暫停跑馬燈',
ariaLabelPlay: '播放跑馬燈',
ariaLabelPrev: '向左捲動',
ariaLabelNext: '向右捲動',
controlPosition: 'top-right' // 暫停鍵置於右上角
});
所有可用的設定參數一覽:
| 參數名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
velocity |
Number | 2 |
每幀移動的像素數(速度) |
forward |
Boolean | true |
true = 由右向左,false = 由左向右 |
childNode |
String | '.item' |
每個項目的 CSS 選擇器 |
childWrapper |
String | '.item-wrap' |
項目外層容器的 CSS 選擇器 |
pauseOnHover |
Boolean | true |
滑鼠移入時暫停動畫 |
autoStart |
Boolean | true |
是否自動開始動畫 |
disableAnimation |
Boolean | false |
完全停用動畫 |
seamlessLoop |
Boolean | true |
啟用無縫循環(複製項目) |
showControls |
Boolean | true |
顯示暫停/播放切換按鈕 |
showNavButtons |
Boolean | true |
顯示向左/向右導航按鈕(SC 2.4.3) |
controlPosition |
String | 'top-right' |
暫停/播放按鈕的水平對齊:top-right(預設)、
top-left。
WCAG 2 AA:切換鈕一律置於跑馬燈最上方(SC 2.2.2 / 2.4.3)。
|
controlSize |
Number | 36 |
按鈕大小(px);建議不低於 44px(觸控目標,SC 2.5.5) |
useFontAwesome |
Boolean | false |
使用 FontAwesome 圖示(需自行引入 FA) |
iconPause |
String | '⏸' |
播放中時顯示的圖示(點擊可暫停) |
iconPlay |
String | '▶' |
暫停中時顯示的圖示(點擊可播放) |
iconPrev |
String | '◀' |
向左導航按鈕圖示 |
iconNext |
String | '▶' |
向右導航按鈕圖示 |
ariaLabel |
String | 'Scrolling content' |
容器的 ARIA 標籤(SC 1.3.1 / 4.1.2) |
ariaLabelPause |
String | 'Pause scrolling' |
動畫播放中時,切換鈕的 aria-label(描述「將暫停」) |
ariaLabelPlay |
String | 'Play scrolling' |
動畫暫停中時,切換鈕的 aria-label(描述「將播放」) |
ariaLabelPrev |
String | 'Scroll left' |
向左按鈕的 aria-label |
ariaLabelNext |
String | 'Scroll right' |
向右按鈕的 aria-label |
ariaLive |
String | 'off' |
ARIA live 區域設定 |
reduceMotion |
Boolean | false |
強制減少動態(自動偵測 prefers-reduced-motion) |
onComplete |
Function | null |
初始化完成後的回呼函式 |
onPause |
Function | null |
暫停時的回呼函式 |
onResume |
Function | null |
恢復播放時的回呼函式 |
透過實例呼叫以下方法來控制跑馬燈行為:
| 方法 | 參數 | 說明 |
|---|---|---|
.pause(manual) |
manual: Boolean |
暫停動畫。傳入 true 表示手動暫停(hover 不會自動恢復) |
.resume(manual) |
manual: Boolean |
恢復動畫。傳入 true 表示手動恢復 |
.toggle() |
無 | 切換暫停/播放狀態 |
.setVelocity(v) |
v: Number |
動態設定速度(每幀像素數) |
.setDirection(forward) |
forward: Boolean |
true=向左,false=向右 |
.destroy() |
無 | 銷毀實例、移除事件監聽與複製的 DOM 元素 |
var marquee = new TadLoop('#my-marquee', { velocity: 3 });
// 暫停
marquee.pause(true);
// 恢復
marquee.resume(true);
// 切換
marquee.toggle();
// 加速
marquee.setVelocity(5);
// 改變方向
marquee.setDirection(false); // 改為向右
// 銷毀
marquee.destroy();
| 控制項 | 按鍵 | 功能 |
|---|---|---|
| 暫停/播放按鈕 | Tab → Space 或 Enter |
切換暫停/播放(按鈕位於跑馬燈最上方,為第一個 Tab 停點) |
| 捲動項目連結 | Tab |
焦點移入項目時動畫自動暫停;焦點離開後恢復播放 |
| ◀ 向左按鈕 | Tab → Space 或 Enter |
自動暫停並將內容向左移動 20px(位於項目之後) |
| 向右 ▶ 按鈕 | Tab → Space 或 Enter |
自動暫停並將內容向右移動 20px |
| 容器(後備) | ← / → 方向鍵 |
暫停狀態下,於容器內任意位置微調捲動位置 |
控制按鈕依 DOM 順序排列,Tab 鍵焦點路徑為: 暫停/播放 → 捲動項目 → 向左 → 向右, 符合上至下、左至右的閱讀邏輯,輔助科技可正確報讀每個控制項的名稱與狀態。
pauseOnHover: true)prefers-reduced-motion 系統設定,停用動畫role="region"、aria-label、aria-livearia-pressed(true=已暫停)
與動態 aria-label(描述「下一步動作」),螢幕閱讀器報讀清晰
aria-label(可本地化)aria-hidden="true",複製項目內的連結設為 tabindex="-1":focus-visible 輪廓(#005fcc,對比 7.6:1):focus-visible 輪廓forced-colors: active)如果你原本使用 jQuery 版本的 grouploop,以下是遷移對照:
$('.promo-carousel').grouploop({
velocity: 2,
forward: true,
pauseOnHover: true
});
var marquee = new TadLoop('.promo-carousel', {
velocity: 2,
forward: true,
pauseOnHover: true,
// 加入 WCAG 2 AA 無障礙標籤
ariaLabel: '促銷活動跑馬燈',
ariaLabelPause: '暫停跑馬燈',
ariaLabelPlay: '播放跑馬燈'
});
| 項目 | jQuery grouploop | TadLoop |
|---|---|---|
| 依賴 | 需要 jQuery | 零依賴 |
| 初始化方式 | $(selector).grouploop(opts) |
new TadLoop(selector, opts) |
| 控制方式 | 無公開 API | 完整公開 API(pause / resume / toggle / destroy 等) |
| 暫停按鈕位置 | 無 | 固定於跑馬燈最上方(WCAG 2 AA SC 2.2.2) |
| 焦點順序 | 不支援 | 暫停/播放 → 項目 → 向左 → 向右(SC 2.4.3) |
| 鍵盤焦點暫停 | 不支援 | 焦點移入項目時自動暫停(SC 2.2.2 / 2.1.1) |
| FontAwesome | 預設使用 | 可選,預設使用 emoji 文字 |
| CSS 寬度 | 需手動設定 width: 200%/400% |
自動使用 width: max-content |
| 銷毀/清理 | 不支援 | .destroy() 完整清理 |
<!-- 引入 FontAwesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<script>
var marquee = new TadLoop('#my-marquee', {
useFontAwesome: true // 使用 FA 圖示
});
</script>
var marquee = new TadLoop('#my-marquee', {
onComplete: function() {
console.log('TadLoop 初始化完成!');
},
onPause: function() {
console.log('動畫已暫停');
},
onResume: function() {
console.log('動畫已恢復');
}
});
var marquee1 = new TadLoop('#marquee-top', {
velocity: 1,
forward: true,
ariaLabel: '頂部促銷跑馬燈',
ariaLabelPause: '暫停頂部跑馬燈',
ariaLabelPlay: '播放頂部跑馬燈'
});
var marquee2 = new TadLoop('#marquee-bottom', {
velocity: 3,
forward: false,
controlPosition: 'top-left',
ariaLabel: '底部公告跑馬燈',
ariaLabelPause: '暫停底部跑馬燈',
ariaLabelPlay: '播放底部跑馬燈'
});
var marquee = new TadLoop('#my-marquee', {
autoStart: false,
showControls: true,
ariaLabel: '促銷跑馬燈(未播放)'
});
// 在某個事件後啟動
document.getElementById('startBtn').addEventListener('click', function() {
marquee.resume(true);
});
| 瀏覽器 | 最低版本 |
|---|---|
| Chrome | 49+ |
| Firefox | 44+ |
| Safari | 10+ |
| Edge | 14+ |
| iOS Safari | 10+ |
| Android Chrome | 49+ |
核心技術需求:requestAnimationFrame、CSS transform、Object.assign(或 polyfill)。