iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 29

[Day29] - Countdown Timer(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

做一個網頁倒數裝置,設有 5 種常用快速選項,也可以讓使用者自行輸入,並會將倒數時間顯示於畫面及分頁標籤上

觀察 index-Start.html,會發現有四個button每都有data-time=""屬性/單位為秒,而另外有一個form裡面可以供使用者輸入時間/單位為分鐘,並算出倒數時間及結束時間方別渲染於對應名稱的 html 欄位中

  1. 選取我們需要操作到的節點
//  所有秒數按鈕的NodeList
const buttons = document.querySelectorAll(".timer__button");
//  使用者自行輸入表單的節點
const customForm = document.querySelector("#custom");
//  渲染結束時間的節點
const endTime = document.querySelector(".display__end-time");
//  渲染剩餘時間的節點
const timeLeft = document.querySelector(".display__time-left");
  1. 儲存週期執行的變數:因為我們必須顯示倒數時間於document.titleh1中,且是每秒更新,因此需要用到setInterval() function週期執行,且時間到後要清除週期執行,因此我們於全域宣告一個變數來存放設定的週期執行。
let interval;
  1. 添加監聽器:接著對按鈕們及表單分別添加對應的事件監聽器
//  按鈕對應點擊事件
buttons.forEach((button) => {
  button.addEventListener("click", handleClick);
});
//  表單對應送出事件
customForm.addEventListener("submit", handleSubmit);
  1. 事件處理函式:因為按鈕的時間單位是秒數所以直接傳入即可,表單的時間單位為分鐘所以還要乘以 60 換算為秒,兩者於元素內取得的值都為字串,因此我們先主動轉型。
function handleClick(e) {
  // 取得秒數轉數字、帶入倒數計時器函式
  const seconds = Number(this.dataset.time);
  timer(seconds);
}

function handleSubmit(e) {
  //  因為提交有我們不需要的預設行為,所以我們把預設行為關掉
  e.preventDefault();
  // 取得分鐘數先換算秒數再轉數字、帶入倒數計時器函式
  const seconds = Number(this.minutes.value) * 60;
  // 當輸入的值轉成數字不是NaN才帶入執行
  if (!isNaN(seconds)) timer(seconds);
  //  因為預設行為關了,所以送出後表單不會清掉,因此需要手動清掉
  this.reset();
}
  1. 倒數計時器 timer 函式
function timer(seconds) {
  //  考量到需要重設計時器,因此觸發後我們統一清除上一次的週期執行
  clearInterval(interval);
  //  依照輸入的秒數換算結束時間的時間戳記
  const endTimStamp = Date.now() + seconds * 1000;
  //  一觸發就要先渲染一次剩餘時間於畫面上
  displayTimeLeft(endTimStamp);
  //  渲染結束時間畫面上的函式
  displayEndTime(endTimStamp);
  //  設定每秒週期執行更新剩餘時間於畫面上
  interval = setInterval(() => {
    displayTimeLeft(endTimStamp);
  }, 1000);
}
  1. 畫面渲染函式:因為我們想要的時間格式為 00:00 因此當後面的時間單位小於 10 時,需要在前面再填一個零
//  將'結束時間的時間戳記'與'最新的時間戳記'記算剩餘時間並轉換我們指定格式字串
//  顯示於<h1 class="display__time-left">中
function displayTimeLeft(endTimeStamp) {
  const nowTimeStamp = Date.now();
  // 換算剩餘時間因為每秒執行因此有可能現在時間大於結束時間變成-1所以比0小都會是0
  const leftSeconds = Math.max(
    Math.round((endTimeStamp - nowTimeStamp) / 1000),
    0
  );
  const minutes = Math.floor(leftSeconds / 60);
  const seconds = leftSeconds % 60;
  //  剩餘時間需要在分頁標籤及畫面上都顯示
  const timeString = `${minutes}:${seconds < 10 ? "0" + seconds : seconds}`;
  document.title = timeString;
  timeLeft.textContent = timeString;
  //  如果現在時間已超過結束間則清除週期執行
  if (nowTimeStamp > endTimeStamp) {
    clearInterval(interval);
  }
}
//  將結束時間的時間戳記轉換我們指定格式字串
//  顯示於<p class="display__end-time">中
function displayEndTime(endTimeStamp) {
  const endHours = new Date(endTimeStamp).getHours();
  const endMinutes = new Date(endTimeStamp).getMinutes();
  endTime.textContent = `Be back At ${endHours}:${
    endMinutes < 10 ? 0 : ""
  }${endMinutes}`;
}

延伸功能:如果各位有興趣可以自行添加一個暫停、繼續的按鈕,以及清除的按鈕(而不是需要在表單內填 0 或等時間到才能停止),也可以設定當時間到之後播放一段 mp3 提示聲,這樣就更像外面看到的番茄盅軟體

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/

上一篇
[Day28] - Video Speed Controller(JS30 x 鐵人 30 x MDN)
下一篇
[Day30] - Whack A Mole(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言