🔄 TadLoop

純 JavaScript 無縫循環跑馬燈 / Marquee

v1.1.0 ・ 零依賴 ・ WCAG 2 AA

🎬 即時展示 Live Demo

以下是一個實際運作的範例,可使用右上角的暫停按鈕或下方按鈕控制:

⚡ 快速開始

1. 引入檔案

<link rel="stylesheet" href="tad_loop.css">
<script src="tad_loop.js"></script>

2. HTML 結構

<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>

3. 初始化(含 WCAG 2 AA 無障礙設定)

var marquee = new TadLoop('#my-marquee', {
  velocity: 2,
  forward: true,
  pauseOnHover: true,
  // WCAG 2 AA 無障礙設定
  ariaLabel: '促銷活動跑馬燈',
  ariaLabelPause: '暫停跑馬燈',
  ariaLabelPlay:  '播放跑馬燈',
  ariaLabelPrev:  '向左捲動',
  ariaLabelNext:  '向右捲動',
  controlPosition: 'top-right'   // 暫停鍵置於右上角
});

⚙️ 設定選項 Options

所有可用的設定參數一覽:

參數名稱 型別 預設值 說明
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 恢復播放時的回呼函式

🛠 公開方法 API Methods

透過實例呼叫以下方法來控制跑馬燈行為:

方法 參數 說明
.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();

⌨️ 鍵盤操作

♿ WCAG 2 AA 焦點順序(SC 2.4.3) 以 Tab 鍵依序聚焦: ⏸ 暫停/播放 → 捲動項目連結 → ◀ 向左 → 向右 ▶ —— 依上至下、左至右,與視覺呈現一致,輔助科技可正確報讀。
控制項 按鍵 功能
暫停/播放按鈕 Tab → Space 或 Enter 切換暫停/播放(按鈕位於跑馬燈最上方,為第一個 Tab 停點)
捲動項目連結 Tab 焦點移入項目時動畫自動暫停;焦點離開後恢復播放
◀ 向左按鈕 Tab → Space 或 Enter 自動暫停並將內容向左移動 20px(位於項目之後)
向右 ▶ 按鈕 Tab → Space 或 Enter 自動暫停並將內容向右移動 20px
容器(後備) ← / → 方向鍵 暫停狀態下,於容器內任意位置微調捲動位置

♿ 無障礙特性 Accessibility(WCAG 2 AA)

焦點順序(SC 2.4.3 Focus Order)

控制按鈕依 DOM 順序排列,Tab 鍵焦點路徑為: 暫停/播放 → 捲動項目 → 向左 → 向右, 符合上至下、左至右的閱讀邏輯,輔助科技可正確報讀每個控制項的名稱與狀態。

暫停機制(SC 2.2.2 Pause, Stop, Hide)

名稱、角色、值(SC 4.1.2 Name, Role, Value)

焦點可見性(SC 2.4.7 Focus Visible)

色彩對比(SC 1.4.3 Contrast)

🔄 從 jQuery grouploop 遷移

如果你原本使用 jQuery 版本的 grouploop,以下是遷移對照:

Before (jQuery)

$('.promo-carousel').grouploop({
  velocity: 2,
  forward: true,
  pauseOnHover: true
});

After (TadLoop)

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

<!-- 引入 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);
});

🌐 瀏覽器支援

瀏覽器 最低版本
Chrome49+
Firefox44+
Safari10+
Edge14+
iOS Safari10+
Android Chrome49+

核心技術需求:requestAnimationFrame、CSS transform、Object.assign(或 polyfill)。