iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Modern Web

Modern Web × AI《拖延怪日記》:語錄陪伴擺脫拖延系列 第 15

【Day 15】- 入門 JavaScript 網頁架設:多時段狀態匯總與分支導航

  • 分享至 

  • xImage
  •  

摘要
Day 14 我們完成了「精神狀態小測驗」:使用者能分別填寫早/午/晚三個時段的精神狀態,且會自動記住上次勾選。
今天 Day 15,我們把小測驗串接到下一步的「行動分支頁」,在轉場前加上一段「今日整體回顧」,讓使用者先看到自己的能量分布,再做選擇:

  • Start now(馬上開始!):鼓勵趁精神好立刻行動
  • Later(我有點累…):溫柔引導先休息、晚點再安排

這一步把 Day 12–14 的情緒與能量資訊轉化為行動,從「知道」跨到「去做」。

為什麼要加條件分支與任務卡移動?

  • 決策前的自我覺察:把三個時段的精神狀態彙整成一句「今日整體回顧」,更容易做出當下最合適的選擇。
  • 降低阻力:給出兩個明確的行動按鈕(Now / Later),避免卡在「等一下再說」的模糊地帶。
  • 可延伸:未來可以把「Start now」接到番茄鐘 / 任務切片;「Later」接到行程規劃 / 推播提醒。

學習重點

  • 跨頁資料承接:把 Day 14 存在 LocalStorage 的三時段狀態,在 Day 15 組成一句可讀性高的回顧字串。
  • 狀態 → 行動:用「分支頁」把情緒/能量資訊轉換為可執行的下一步。
  • a11y 與焦點:切頁時確保焦點落點正確,ARIA live 提升整體體驗。

核心流程

  1. 在小測驗頁新增「提交」按鈕:即使只勾了部分時段,也能提交。若完全沒勾,會禮貌地再次確認。
  2. 彙整當日精神狀態:把 morning/afternoon/evening 的已填項目組成一句「今天整體回顧 → …」,顯示在分支頁最上方。
  3. 切換到「行動分支頁」:提供 Start now / Later 兩種分支,並即時顯示友善回饋文字。
  4. 維持無障礙體驗:切換頁面時把焦點放在適合的元素(例如分支頁的第一個按鈕),ARIA live 區塊朗讀最新資訊。

實作

HTML:小測驗提交 & 行動分支頁
註:其他區塊(Day 12–14)維持不變,此處僅展示 Day 15 新增的 HTML。

<!-- ★ Day15-NEW:小測驗提交,前往條件分支頁 -->
<div style="margin-top:0.5rem;">
  <button id="moodSubmitBtn" type="button" aria-controls="moodDecisionSection">
    submit
  </button>
</div>

<!-- Day 15 新增:條件分支頁 -->
<section id="moodDecisionSection" hidden>
  <p id="todayRecap" aria-live="polite"></p> <!-- ★ 今日整體回顧(新) -->
  <h2>要不要在精神最好的時段完成任務?</h2>
  <button id="btnStartNow">馬上開始!</button>
  <button id="btnLater_2">我有點累…</button>
  <p id="decisionFeedback" aria-live="polite"></p>
</section>

JavaScript:
註:safeParseJSON、PERIOD_LABEL、小測驗的 radio 事件綁定與 showAggregateFeedback 等 Day 14 內容均沿用,不在此重複。

JS:DOM 參照(新增):

// === Day 15 新增節點 ===
const moodDecisionSection = document.getElementById('moodDecisionSection');
const btnStartNow         = document.getElementById('btnStartNow');
const decisionFeedback    = document.getElementById('decisionFeedback');
const moodSubmitBtn       = document.getElementById('moodSubmitBtn');
const btnLater_2          = document.getElementById('btnLater_2');

JS:提交小測驗 ➜ 產生「今日整體回顧」並切頁:

// 取得並渲染今日整體回顧(Day15-NEW)
function getMoodData() {
  return safeParseJSON(localStorage.getItem(MOOD_KEY), {});
}
function renderTodayRecap() {
  const el = document.getElementById('todayRecap');
  if (!el) return;
  const moodData = getMoodData();
  const parts = ['morning','afternoon','evening']
    .filter(p => moodData[p])
    .map(p => `${PERIOD_LABEL[p]}:${moodData[p]}`);
  el.textContent = parts.length
    ? `今天整體回顧 → ${parts.join('; ')}`
    : '今天尚未填寫精神狀態(可稍後在小測驗補填)';
}

// 小測驗提交(Day15-NEW)
moodSubmitBtn?.addEventListener('click', () => {
  const moodData = safeParseJSON(localStorage.getItem(MOOD_KEY), {});
  const hasAny = ['morning','afternoon','evening'].some(p => !!moodData[p]);
  if (!hasAny && !confirm('今天尚未選任何時段的狀態,仍要前往任務提問頁嗎?')) return;

  renderTodayRecap();       // 先填好回顧
  showPage('moodDecision'); // 再切到分支頁
});

JS:頁面切換支援新分支頁:

// ---- 顯示/切換主要區塊(加入 Day15 分支)----
function showPage(page) {
  // 先隱藏所有主要區塊(存在才動作,避免 null)
  if (formSection)         formSection.hidden = true;
  if (historySection)      historySection.hidden = true;
  if (moodTestSection)     moodTestSection.hidden = true;
  if (moodDecisionSection) moodDecisionSection.hidden = true;  // Day15
  if (taskScheduleSection) taskScheduleSection.hidden = true;  // Day15

  // 小工具:嘗試聚焦標題;若失敗,將區塊設為可聚焦並聚焦
  function focusOrFallback(sectionEl, titleElId) {
    const titleEl = titleElId ? document.getElementById(titleElId) : null;
    if (titleEl && typeof titleEl.focus === 'function') {
      titleEl.focus();
    } else if (sectionEl) {
      sectionEl.setAttribute('tabindex', '-1');
      sectionEl.focus && sectionEl.focus();
    }
  }

  // 依 page 顯示 + 聚焦
  if (page === 'history') {
    if (historySection) {
      historySection.hidden = false;
      if (typeof renderHistory === 'function') renderHistory();
      focusOrFallback(historySection, 'history-title'); // 與較早版一致
    }
  } else if (page === 'moodTest') {
    if (moodTestSection) {
      moodTestSection.hidden = false;
      focusOrFallback(moodTestSection, 'mood-title');   // 與較早版一致
    }
  } else if (page === 'moodDecision') {                 // Day15
    if (moodDecisionSection) {
      moodDecisionSection.hidden = false;
      moodSubmitBtn?.setAttribute('aria-expanded', 'true');
      // 這頁以按鈕為第一焦點,若不存在退而聚焦區塊本身
      const first = document.getElementById('btnStartNow');
      if (first && typeof first.focus === 'function') first.focus();
      else focusOrFallback(moodDecisionSection);
    }
  } else { // 'home' or default
    if (formSection) {
      formSection.hidden = false;
      moodSubmitBtn?.setAttribute('aria-expanded', 'false');
      if (typeof focusFirstField === 'function') focusFirstField();
      else {
        formSection.setAttribute('tabindex', '-1');
        formSection.focus && formSection.focus();
      }
    }
  }
}

JS:分支頁按鈕的即時回饋:

// ---- 條件分支互動(Day15-NEW)----
btnStartNow?.addEventListener('click', () => {
  decisionFeedback.textContent = "太好了!那就趁精神好,完成任務吧!💪";
  // TODO:未來可導到「番茄鐘」或「切片任務」頁
});
btnLater_2?.addEventListener('click', () => {
  decisionFeedback.textContent = "沒關係,先休息一下,等等再安排。😌";
  // TODO:未來可導到「行程規劃」或「稍後提醒」頁
});

無障礙 / UX 細節

  • 焦點管理:切頁後自動把焦點放在分支頁的第一個主要按鈕,鍵盤與報讀器都能無縫操作。
  • 禮貌提醒:完全沒填小測驗就提交時,先顧及使用者的意願(confirm),避免強制。
  • 逐步回饋:aria-live="polite" 讓回顧與決策回饋在同一頁自然朗讀,不打斷使用者。

驗證

  1. 進到小測驗頁,勾任一時段(或全部),按 submit
    → 進入分支頁,頂端出現「今天整體回顧」且內容與勾選一致。
  2. 三個時段都沒勾直接按 submit
    → 會跳出確認;選「取消」留在小測驗,選「確定」則進分支頁並顯示「尚未填寫」訊息。
  3. 在分支頁按 馬上開始! / 我有點累…
    → 下方的 decisionFeedback 文字即時更新。
  4. 使用螢幕報讀(VoiceOver / NVDA)
    → 切換到分支頁時焦點落在第一個可操作按鈕,todayRecap 與 decisionFeedback 由於是 aria-live="polite" 能被朗讀。

常見錯誤 & 排查

  • 按 submit 沒反應
    檢查是否正確抓到 #moodSubmitBtn 與已綁定 addEventListener('click', …)。
  • 分支頁顯示空白
    可能忘記在 showPage 內處理 page === 'moodDecision';或分支頁 section 還在 hidden 狀態。
  • 今日整體回顧總是空
    確認 Day 14 的 radio name 是否分別為 morning/afternoon/evening,且 saveMood(name, value) 有被觸發。

上一篇
【Day 14】- 入門 JavaScript 網頁架設:精神狀態小測驗(radio button 單選)
下一篇
【Day 16】- 入門 JavaScript 網頁架設:appendChild 移動 DOM(任務卡 × 時段格子)
系列文
Modern Web × AI《拖延怪日記》:語錄陪伴擺脫拖延17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言