🎯 系列目標:用 30 天時間,從零開始打造一個專屬輔大學生的課表生成 Chrome 擴充功能
👨💻 作者:輔大智慧資安 412580084
📅 Day 10:Chrome Extension 實作篇 - Popup 按鈕事件處理
昨天我們建立了 popup.js 的基礎架構,今天我們要讓按鈕真正具備功能!我們將學習如何處理按鈕點擊事件,並提供使用者回饋。
今天我們要完成:
我們將要對生成課表按鈕添加重複點擊禁用的功能:
// 處理生成課表按鈕點擊
function handleGenerateClick() {
  console.log('🔘 生成課表按鈕被點擊');
  
  // 顯示載入狀態
  showStatus('正在準備生成課表...', 'info');
  
  // 禁用按鈕防止重複點擊
  const generateButton = document.getElementById('generateButton');
  generateButton.disabled = true;
  generateButton.textContent = '⏳ 處理中...';
  
  // 模擬處理過程(3秒後完成)
  setTimeout(() => {
    // 恢復按鈕狀態
    generateButton.disabled = false;
    generateButton.textContent = '📊 開始生成課表';
    
    // 顯示成功訊息
    showStatus('課表生成成功!', 'success');
    
    console.log('✅ 課表生成完成');
  }, 3000);
}
設定按鈕也與生成課表按鈕一樣:
// 處理設定按鈕點擊
function handleSettingsClick() {
  console.log('🔘 設定按鈕被點擊');
  
  showStatus('正在載入設定...', 'info');
  
  // 模擬載入設定(2秒後完成)
  setTimeout(() => {
    showStatus('設定:自動儲存已開啟', 'success');
    
    // 3秒後回到準備狀態
    setTimeout(() => {
      showStatus('準備就緒', 'success');
    }, 3000);
  }, 2000);
}
因為我們在按鈕添加了過度狀態 info,所以要在showStatus函數中將原本:
statusDiv.classList.remove('success', 'error');
修改成:
statusDiv.classList.remove('success', 'error', 'info');
然後再將原 if判斷式 改成:
statusDiv.classList.add(type);
function showLoadingStatus(message) {
  const statusDiv = document.getElementById('status');
  if (statusDiv) {
    statusDiv.innerHTML = `${message} <span class="loading">⏳</span>`;
    statusDiv.style.display = 'block';
    statusDiv.classList.remove('success', 'error');
    statusDiv.classList.add('info');
  }
}
在 popup.html 的 <style> 區塊中添加新樣式:
.status.info {
  background-color: #e3f2fd;
  color: #1976d2;
}
.loading {
  animation: spin 1s linear infinite;
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}
我們會發現生成與設定按鈕在禁用與啟用的程式碼有重複,所以應該要對按鈕狀態封裝以方便管理:
// 設定按鈕載入狀態
function setButtonLoading(buttonId, loadingText) {
  const button = document.getElementById(buttonId);
  if (button) {
    button.disabled = true;
    button.dataset.originalText = button.textContent;
    button.textContent = loadingText;
  }
}
// 恢復按鈕正常狀態
function resetButton(buttonId) {
  const button = document.getElementById(buttonId);
  if (button) {
    button.disabled = false;
    button.textContent = button.dataset.originalText || button.textContent;
  }
}
使用新的按鈕狀態管理重寫1.1 1.2事件處理使其更簡潔:
// 改進的生成課表按鈕處理
function handleGenerateClick() {
  console.log('🔘 生成課表按鈕被點擊');
  
  // 設定按鈕載入狀態
  setButtonLoading('generateButton', '⏳ 處理中...');
  
  // 顯示載入狀態
  showLoadingStatus('正在準備生成課表...');
  
  // 模擬處理過程
  setTimeout(() => {
    // 恢復按鈕狀態
    resetButton('generateButton');
    
    // 顯示成功訊息
    showStatus('課表生成成功!', 'success');
    
    console.log('✅ 課表生成完成');
  }, 3000);
}
// 改進的設定按鈕處理
function handleSettingsClick() {
  console.log('🔘 設定按鈕被點擊');
  
  setButtonLoading('settingsButton', '⏳ 載入中...');
  showLoadingStatus('正在載入設定...');
  
  setTimeout(() => {
    resetButton('settingsButton');
    showStatus('設定:自動儲存已開啟', 'success');
    
    setTimeout(() => {
      showStatus('準備就緒', 'success');
    }, 3000);
  }, 2000);
}
更新 popup.js 檔案:
更新 popup.html 的 CSS:
<style> 區塊重新載入擴充功能:
測試按鈕功能:
點擊「開始生成課表」按鈕
觀察按鈕變成「處理中...」並被禁用
等待 3 秒看到成功訊息
檢查 Console 輸出:
 
📁 fju-schedule-extension/
├── 📄 manifest.json     ← 擴充功能設定
├── 📄 background.js     ← 背景腳本
├── 📄 popup.html        ← 使用者介面(需更新 CSS)
└── 📄 popup.js          ← 彈出視窗邏輯(今天完成事件處理)
🔗 知識銜接:今天我們讓按鈕具備了真正的功能和使用者回饋,明天將學習如何讓 popup 與 background script 進行消息傳遞。
🎯 下集預告:Day 11 - Popup 與 Background 消息傳遞 📡