在 Vue 中設定 input date 輸入的日期範圍

閱讀時間約 10 分鐘

問題描述

  在設計輸入日期的表單時,有時我們會需要限定時間範圍。

  像是常見的網銀帳戶記錄,很多就有類似從今天開始前的一年內為查詢範圍的限制,有時候就算在說明文字中已說明資料範圍僅有一年內的資料,還是有可能會有用戶去選到超過一年內的日期。

  這種時候直接禁止選取超過一年內的日期,可以避免使用者不小心進行錯誤的操作。


實作案例

  要設定 <input type=”date”> 的範圍其實用原生的 JS 就能做到,只需要增加 max 或 min 的屬性即可:

<html>
<head>
<title>Date Range Example</title>
</head>
<body>
<input type="date" id="dateInput">

<script>
// 獲取今日日期
const today = new Date();
// 設定最大值為今日
const maxDate = today.toISOString().split("T")[0];
// 設定最小值為一年前
today.setFullYear(today.getFullYear() - 1);
const minDate = today.toISOString().split("T")[0];

// 設置 input 的最小值和最大值
document.getElementById('dateInput').max = maxDate;
document.getElementById('dateInput').min = minDate;
</script>
</body>
</html>


  而在 Vue 中就可以這樣寫:

<template>
<input type="date" :max="maxDate" :min="minDate">
</template>

<script setup>
import { ref, onMounted } from 'vue';

const today = new Date;
const maxDate = ref('');
const minDate = ref('');

function setDateRange() {
maxDate.value = today.toISOString().split("T")[0];
today.setFullYear(today.getFullYear() - 1);
minDate.value = today.toISOString().split("T")[0];
}

onMounted(() => {
setDateRange();
})


</script>


語法解䆁

input date 屬性:max 與 min

  max 與 min 都是 <input type=”date”> 本身就擁有的屬性,從 MDN 中的說明可以看到,設定的時候需符合 yyyy-mm-dd 這樣的日期格式。因此如果有一個已知的固定時間,也是可以直接把 max 或 min 的值寫死。


new Date():動態取得今日日期

  因為在這個案例中,要取得的日期範圍是「從今日開始到一年前」這個動態的區間,因此可以用new Date() 這個方法來取得現在日期和時間的 Date 物件。

  console.log() 之後看到的內容會是:Tue Oct 22 2024 00:00:00 GMT+0800 (台北標準時間) 這樣的格式,而我們需要讓它轉成 2024-10-22 才能被我們的 max 給接受,因此需要進行下一步:


.toISOString():把日期格式轉成 YYYY-MM-DDTHH:mm:ss.sssZ

  toISOString() 是 Date 物件的一個內建方法,他會返回一個 ISO 8601(國際標準化組織的日期和時間的表示方法) 格式的字符串,但要注意一個問題是它返回的值會是 UTC 時區的值,也就是以台灣 UTC+8 來說,它的 HH 會比我們使用當下要少 8 小時,假設我們使用的時間在早上八點以前,那麼得到的 max 日期就不會是當天,而是前一天。

  如果想避免這個情況發生,也可以用函式手動將 Date 物件進行格式化,格式化的函式放到最後再來說明。


.split(“T”)[0]:提取 YYYY-MM-DD

  前面已經透過 .toISOString() 把 Date 物件轉成 YYYY-MM-DDTHH:mm:ss.sssZ 格式了,但我們需要的部份其實只有 YYYY-MM-DD 這部份而已,因此用字串內建的方法 split(),以T這個字為切割點,把整個字串切成了陣列:[ ‘YYYY-MM-DD‘, ‘HH:mm:ss.sssZ‘ ],而其中第 0 項的 ‘YYYY-MM-DD’ 就是我們需要的部份。

  其實用另一個字串的內建方法 slice() 也能達到一樣的效果,因為 YYYY-MM-DDTHH:mm:ss.sssZ 的格式是固定的,我們需要的 YYYY-MM-DD 區塊必定是字串的前十個字,因此用 .slice(0,10) 也可以獲得我們需要的部份,寫法會像這樣:

function setDateRange() {
maxDate.value = today.toISOString().slice(0,10);
today.setFullYear(today.getFullYear() - 1);
minDate.value = today.toISOString().slice(0,10);
}


如何手動格式化 Date 物件

  為了避免時差導致日期錯誤,我們可以設計一個函式來將 Date 物件手動格式化成 YYYY-MM-DD 的格式,具體寫法如下:

const today = new Date()

function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}

formatDate(today);


  這個函式的思路很簡單,就是分別把日期物件的年、月、日提取出來之後,再重新組合成我們要的格式。其中值得注意的部份有兩點:

  1. 用 getMonth() 得到的值是 monthIndex,因此要再 +1 才會是正確的月份。
  2. 用 getFullYear()、getMonth()、getDate() 取得的值都是數字,如果月或日是個位數的話需要補零,為了補零就需要先把數字轉成字串。轉成字串之後再用 padStart() 來確保字符串至少有兩個字符,若不足兩個字符的時候就在前面補零。


  套用到案例中的話,就會是這樣:

<template>
<input type="date" :max="maxDate" :min="minDate">
</template>

<script setup>
import { ref, onMounted } from 'vue';

const today = new Date;
const maxDate = ref('');
const minDate = ref('');

function setDateRange() {
maxDate.value = formatDate(today);
today.setFullYear(today.getFullYear() - 1);
minDate.value = formatDate(today);
}

function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}

onMounted(() => {
setDateRange();
})


</script>


參考資料

MDN - <input type=”date”>

MDN - Date

JavaScript 之旅 (5):String method - padStart & padEnd

AI 工具 - Microsoft Copilot



4會員
15內容數
我是佧佧,歡迎來到我的術式領域。 讓我們一起磨練技術、學習程式, 在前端的領域中不斷成長吧!
留言0
查看全部
發表第一個留言支持創作者!
佧佧的術式領域 的其他內容
  在寫程式的時候,我們常會需要針對不同情況來執行不同的工作。這個時候就會用到條件判斷式 ( if statement )
  在 RWD 設計當中,很常見到某些區塊在大螢幕上要顯示多欄、在小螢幕上要顯示單欄。用 Bootstrap 的排版(格線系統)可以設定不同裝置要呈現的大小及欄位。但若不依靠 Bootstrap 來做,而是自己撰寫 CSS 的話,或許可以試著用設定最大或最小寬度來完成自適應。
  先前在開發的專案中,需要一個上傳圖片後可以看即時看到預覽的功能。再加上這個功能在許多地方都會用到,因此試著製作了一個可以獨立運行的組件。
Nuxt 是什麼? 總之先學再說! 初見Nuxt,需要的環境?怎麼安裝? 快來看看,從今天開始學 Nuxt !
對於想要嘗試學習前端技術,卻還沒下定決心或不知道是否合適的初心者,六角的體驗營就像是前端開發的新手村,提供基礎的新手教學,讓你對前端有基礎的概念;教你最基本的心法,讓你去打幾隻簡單的怪累積一點經驗值。
本篇最主要是要了解網站前端工程師究竟是什麼?主要工作內容又有哪些?將從三個部份來說明:前端的工作內容、前端所需的軟實力以及前端所需的硬實力。
  在寫程式的時候,我們常會需要針對不同情況來執行不同的工作。這個時候就會用到條件判斷式 ( if statement )
  在 RWD 設計當中,很常見到某些區塊在大螢幕上要顯示多欄、在小螢幕上要顯示單欄。用 Bootstrap 的排版(格線系統)可以設定不同裝置要呈現的大小及欄位。但若不依靠 Bootstrap 來做,而是自己撰寫 CSS 的話,或許可以試著用設定最大或最小寬度來完成自適應。
  先前在開發的專案中,需要一個上傳圖片後可以看即時看到預覽的功能。再加上這個功能在許多地方都會用到,因此試著製作了一個可以獨立運行的組件。
Nuxt 是什麼? 總之先學再說! 初見Nuxt,需要的環境?怎麼安裝? 快來看看,從今天開始學 Nuxt !
對於想要嘗試學習前端技術,卻還沒下定決心或不知道是否合適的初心者,六角的體驗營就像是前端開發的新手村,提供基礎的新手教學,讓你對前端有基礎的概念;教你最基本的心法,讓你去打幾隻簡單的怪累積一點經驗值。
本篇最主要是要了解網站前端工程師究竟是什麼?主要工作內容又有哪些?將從三個部份來說明:前端的工作內容、前端所需的軟實力以及前端所需的硬實力。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
可能包含敏感內容
選擇你腦海中的數字,本週將會迎來甚麼樣的訊息和自我提醒呢?
Thumbnail
  接續上一篇文章,換言之,不會因為放假不能睡晚而影響到心情,就算六日都早起,週一也可以保持平靜的心情上班。而在新的一年裡,無論是以一月一日或農曆春節過後為起始日,先將計畫安排好,心裡也會有個底,知道這一年要完成什麼計畫。對於工作的內容,可以用兩個角度來思考:         第一,如果是
Thumbnail
好像很久沒有靜下心來寫一篇關於自己的事情,年初抱著很大的熱枕,在心中默默設定了增加文章撰寫數量的目標,卻反而經常收到魔法師稱號,被方格子給提醒。仔細想想自己近年一直囫圇吞棗,把自己給塞滿,Input了一堆卻來不及消化完,我想也是值得好好檢討的一件事情!
Thumbnail
開始決定規律地發表文章,是從去年的十二月開始,陸陸續續也差不多半年了,正好來到一個適合回顧的時機。 以下就簡單的列幾點,在每一項會分別列舉三條做小結當作跟大家、也跟自己聊聊天。如果你對我這段時間寫的內容、介紹的作品、更新的方式等等有其他想法,也歡迎隨時留言和我分享。
Thumbnail
從年初開始,一直沒有好好整理閱讀清單,拖延至今才發現農曆年前的事情感覺像是很遙遠的記憶了,得花些時間挖掘一下才行,變成以「季」為單位的時候,就會發現人的記憶實在不太可靠。
致關於我與日曆的回憶。
Thumbnail
百日百字計畫 #18 一百天一百字的紀錄,練習覺察的百日紀錄。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
可能包含敏感內容
選擇你腦海中的數字,本週將會迎來甚麼樣的訊息和自我提醒呢?
Thumbnail
  接續上一篇文章,換言之,不會因為放假不能睡晚而影響到心情,就算六日都早起,週一也可以保持平靜的心情上班。而在新的一年裡,無論是以一月一日或農曆春節過後為起始日,先將計畫安排好,心裡也會有個底,知道這一年要完成什麼計畫。對於工作的內容,可以用兩個角度來思考:         第一,如果是
Thumbnail
好像很久沒有靜下心來寫一篇關於自己的事情,年初抱著很大的熱枕,在心中默默設定了增加文章撰寫數量的目標,卻反而經常收到魔法師稱號,被方格子給提醒。仔細想想自己近年一直囫圇吞棗,把自己給塞滿,Input了一堆卻來不及消化完,我想也是值得好好檢討的一件事情!
Thumbnail
開始決定規律地發表文章,是從去年的十二月開始,陸陸續續也差不多半年了,正好來到一個適合回顧的時機。 以下就簡單的列幾點,在每一項會分別列舉三條做小結當作跟大家、也跟自己聊聊天。如果你對我這段時間寫的內容、介紹的作品、更新的方式等等有其他想法,也歡迎隨時留言和我分享。
Thumbnail
從年初開始,一直沒有好好整理閱讀清單,拖延至今才發現農曆年前的事情感覺像是很遙遠的記憶了,得花些時間挖掘一下才行,變成以「季」為單位的時候,就會發現人的記憶實在不太可靠。
致關於我與日曆的回憶。
Thumbnail
百日百字計畫 #18 一百天一百字的紀錄,練習覺察的百日紀錄。