摘要
Day 13 我們完成了「互動引導」與「歷史回顧切換」,讓使用者操作體驗更清楚。
今天要加入一個新功能:「精神狀態小測驗」。
透過早上、下午、晚上的三個小問題,讓使用者更了解自己一天中精神的變化。
這能成為後續將製作的「任務排程」基礎,例如:早上高效 → 建議安排行動任務。
<p>
立即顯示「今天整體回顧 → 早上:清醒; 下午:疲憊; 晚上:睏了」等訊息。HTML:
<!-- [Day14-NEW] 精神狀態小測驗 -->
<section id="moodTestSection"
role="region"
aria-labelledby="mood-title"
hidden>
<h2 id="mood-title">精神狀態小測驗</h2>
<p>太好了!你今天踏出了不一樣的一步 💪</p>
<p id="mood-intro">回憶一下,今天這三個時段你的精神狀態怎麼樣?</p>
<!-- 早上 -->
<fieldset aria-describedby="mood-intro">
<legend>早上</legend>
<div>
<input id="m-morning-awake" type="radio" name="morning" value="清醒">
<label for="m-morning-awake">☀️ 清醒</label>
</div>
<div>
<input id="m-morning-ok" type="radio" name="morning" value="普通">
<label for="m-morning-ok">😑 普通</label>
</div>
<div>
<input id="m-morning-drowsy" type="radio" name="morning" value="昏沉">
<label for="m-morning-drowsy">😴 昏沉</label>
</div>
</fieldset>
<!-- 下午 -->
<fieldset aria-describedby="mood-intro">
<legend>下午</legend>
<div>
<input id="m-afternoon-focus" type="radio" name="afternoon" value="高效">
<label for="m-afternoon-focus">🧠 高效</label>
</div>
<div>
<input id="m-afternoon-tired" type="radio" name="afternoon" value="疲憊">
<label for="m-afternoon-tired">☕️ 有點疲憊</label>
</div>
<div>
<input id="m-afternoon-distract" type="radio" name="afternoon" value="分心">
<label for="m-afternoon-distract">🌪️ 分心</label>
</div>
</fieldset>
<!-- 晚上 -->
<fieldset aria-describedby="mood-intro">
<legend>晚上</legend>
<div>
<input id="m-evening-great" type="radio" name="evening" value="精神超好">
<label for="m-evening-great">😈 精神超好</label>
</div>
<div>
<input id="m-evening-chill" type="radio" name="evening" value="想放空">
<label for="m-evening-chill">🛋️ 想放空</label>
</div>
<div>
<input id="m-evening-sleepy" type="radio" name="evening" value="睏了">
<label for="m-evening-sleepy">🌃 睏了</label>
</div>
</fieldset>
<!-- 以 polite 讀出最新整體回饋 -->
<p id="moodFeedback" aria-live="polite"></p>
<div style="margin-top:1rem;">
<button id="btnHistoryFromMood" aria-controls="historySection">歷史回顧 →</button>
</div>
</section>
JavaScript:
// === 既有節點(請確認外部已有下列節點) ===
const formSection = document.getElementById('recordFormSection');
const historySection = document.getElementById('historySection');
const btnYes = document.getElementById('btnYes'); // 互動引導區的「當然!」按鈕
// === Day14 新增節點 ===
const moodTestSection = document.getElementById('moodTestSection');
const moodTitle = document.getElementById('mood-title');
const moodFeedback = document.getElementById('moodFeedback');
const btnHistoryFromMood= document.getElementById('btnHistoryFromMood');
const MOOD_KEY = 'daily_mood';
const PERIOD_LABEL = { morning: '早上', afternoon: '下午', evening: '晚上' };
// ---- 安全 JSON 解析 ----
function safeParseJSON(raw, fallback) {
try { return raw ? JSON.parse(raw) : fallback; }
catch { return fallback; }
}
// ---- 顯示/切換主要區塊 ----
// 主要功能:隱藏舊區塊,只顯示指定的新區塊
// 可選功能(a11y):切換後自動將焦點移到新區塊標題,方便螢幕閱讀器朗讀與鍵盤操作
function showPage(page) {
// 先全部隱藏
if (formSection) formSection.hidden = true;
if (historySection) historySection.hidden = true;
if (moodTestSection) moodTestSection.hidden = true;
// 再依 page 顯示對應的區塊
if (page === 'history') {
if (historySection) {
historySection.hidden = false;
// 若程式已有 renderHistory,這裡呼叫重新渲染清單
if (typeof renderHistory === 'function') renderHistory();
// (可選 a11y)切換後把焦點移到標題,方便螢幕閱讀器朗讀
const h = document.getElementById('history-title');
(h?.focus?.()) || historySection.setAttribute('tabindex', '-1') && historySection.focus();
}
} else if (page === 'moodTest') {
if (moodTestSection) {
moodTestSection.hidden = false;
// (可選 a11y)同樣將焦點放到標題或區塊本身
(moodTitle?.focus?.()) || (moodTestSection.setAttribute('tabindex', '-1'), moodTestSection.focus());
}
} else {
if (formSection) {
formSection.hidden = false;
// 若有 focusFirstField,優先把焦點放到表單的第一個輸入框
if (typeof focusFirstField === 'function') focusFirstField();
// (可選 a11y)否則讓整個表單區塊可聚焦並帶入焦點
else formSection.setAttribute('tabindex', '-1'), formSection.focus();
}
}
}
// ---- 儲存單一時段 ----
function saveMood(period, value) {
const moodData = safeParseJSON(localStorage.getItem(MOOD_KEY), {});
moodData[period] = value;
localStorage.setItem(MOOD_KEY, JSON.stringify(moodData));
}
// ---- 載入並還原所有勾選 ----
function loadMood() {
const moodData = safeParseJSON(localStorage.getItem(MOOD_KEY), {});
Object.keys(moodData).forEach(period => {
const v = moodData[period];
// 使用更穩健的選擇器:屬性值需加引號
const input = document.querySelector(`input[name="${period}"][value="${v}"]`);
if (input) input.checked = true;
});
showAggregateFeedback(moodData); // 載入時就更新整體回饋
}
// ---- (可選)單次改變時即時文字回饋:顯示「某時段:某狀態」 ----
function showFeedback(period, value) {
if (!moodFeedback) return;
moodFeedback.textContent = `你在【${PERIOD_LABEL[period] || period}】覺得:「${value}」`;
}
// ---- (可選)整體回饋摘要:同時顯示三個時段,提升使用者回顧與螢幕報讀體驗 ----
function showAggregateFeedback(moodData) {
if (!moodFeedback) return;
const parts = ['morning', 'afternoon', 'evening']
.filter(p => moodData[p])
.map(p => `${PERIOD_LABEL[p]}:${moodData[p]}`);
moodFeedback.textContent = parts.length
? `今天整體回顧 → ${parts.join('; ')}`
: '(還沒有選擇,先從上面選一個吧)';
}
// ---- 事件綁定 ----
btnYes?.addEventListener('click', () => showPage('moodTest'));
btnHistoryFromMood?.addEventListener('click', () => showPage('history'));
document
.querySelectorAll('#moodTestSection input[type="radio"]')
.forEach(radio => {
radio.addEventListener('change', (e) => {
const { name, value } = e.target;
saveMood(name, value);
showFeedback(name, value); // 單次即時
// 同步更新整體摘要,方便螢幕報讀一次掌握
const moodData = safeParseJSON(localStorage.getItem(MOOD_KEY), {});
showAggregateFeedback(moodData);
});
});
// ---- 頁面載入時恢復狀態 ----
loadMood();