iT邦幫忙

2

純Javascript,使用new Date()製作date picker

  • 分享至 

  • xImage
  •  

剛好有一道面試題目,不能使用 input type=date 或任何現有套件,要做出類似Datepicker的功能
細節需求為

  1. 點擊“<”及“>”切換年、月
  2. 點選日期時,選到之日期須呈現紅底白字
  3. 於日期頁時,點上方月/年時,切換頁面至選擇月份
  4. 於月份頁時,點上方年,切換頁面至選擇年份(10年為一頁)
  5. 可用input輸入,不一定要用點的

** 單純紀錄 **
前面萬年曆製作方法都是參考彭彭老師的課程(連結如下)
https://www.youtube.com/watch?v=Q2x84RdNVUY&t=4133s
講得很清楚、很詳細

CodePen程式碼預覽
https://codepen.io/shih-min-chou/pen/XWerQzq

下方程式碼參考

CSS

body a:hover,
body button:hover {
    text-decoration: none;
    transition: 0.3s all;
    -webkit-transition: 0.3s all;
    -moz-transition: 0.3s all;
    -o-transition: 0.3s all;
    -ms-transition: 0.3s all;
}
.calendar,
.datepicker {
    max-width: 300px;
    margin: auto;
}
.calendar {
    border: 1px solid #cccccc;
    border-radius: 10px;
    padding: 1em;
}
.datepicker input {
    margin: 0;
    font-family: inherit;
    display: block;
    width: 85%;
    height: calc(2.25rem + 2px);
    padding: 0.375rem 0.75rem 0.375rem 2.75rem;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid #ced4da;
    border-radius: 0.25rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.datepicker input:focus {
    color: #495057;
    background-color: #fff;
    border-color: #f28388;
    outline: 0;
    box-shadow: 0 0 0 0.2rem rgba(219, 61, 68, 0.25) ;
}
.datepicker i {
    position: absolute;
}
.icon {
    padding: 16px;
    font-size: larger;
    color: #555555;
}
.title {
    display: flex;
    justify-content: space-between;
    align-items: center;
} 
.title .arrow-control {
    border: none;
    border-radius: 50px;
    background-color: transparent;
    width: 35px;
    height: 35px;
}
.title .arrow-control:hover {
    background-color: #db3d44;
    color: white;
    cursor: pointer;
}
.title .year-month:hover,
.title .year:hover {
    color: #cccccc;
    cursor: pointer;
    transition: 0.3s all;
}
.week .day {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 14.28%;
    font-weight: bold;
}
.list, .week{
    display: flex;
    flex-wrap: wrap;
    width: 100%;
}
.list .date {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 14.28%;
    height: 42px;
    margin-top: 1em;
    /* padding: 7.14%; */
    border: none;
    border-radius: 50px;
    background-color: transparent;
}
.list .date:hover,.list .date:focus,
.list .mon:hover, .list .mon:focus {
    background-color: #db3d44;
    color: white;
    cursor: pointer;
}
.active {
    background-color: #db3d44 !important;
    color: white;
}
.list .fadeout {
    color: #eeeeee;
}
.list .today {
    color: #db3d44;
}
.list .today.active {
    color: white;
}
.list .mon {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 25%;
    height: 50px;
    margin-top: 1em;
    /* padding: 7.14%; */
    border: none;
    border-radius: 50px;
    background-color: transparent;
}
#yearlist .mon:first-child,
#yearlist .mon:last-child {
    color: #9d9d9d;
}
.d-none {
    display: none;
}

HTML

<div class="datepicker">
    <i class="fas fa-calendar-alt icon" onclick="toggleCalendar();"></i>
    <input class="input-field" type="text" id="datepickerInput" placeholder="YYYY-MM-DD"
        onchange="checkDate(this)" onMouseDown="toggleCalendar();"
        onkeyup = "value=value.replace(/[^0-9\-]/g,'')" inputmode="numeric" minlength="8" maxlength="10">
</div>

<div class="d-none" id="calendar_date">
    <div class="title">
        <button class="arrow-control" onclick="preMonth();"><i class="fas fa-chevron-left"></i></button>
        <h4 id="year-month" class="year-month" onclick="showMonth();"></h4>
        <button class="arrow-control" onclick="nextMonth();"><i class="fas fa-chevron-right"></i></button>
    </div>
    <div class="week" id="week"></div>
    <div class="list" id="list"></div>
</div>
<div class="d-none" id="calendar_month">
    <div class="title">
        <button class="arrow-control" onclick="preYear();"><i class="fas fa-chevron-left"></i></button>
        <h4 id="year" class="year" onclick="showYear();"></h4>
        <button class="arrow-control" onclick="nextYear();"><i class="fas fa-chevron-right"></i></button>
    </div>
    <div class="list" id="monlist"></div>
</div>
<div class="d-none" id="calendar_year">
    <div class="title">
        <button class="arrow-control" onclick="preYears();"><i class="fas fa-chevron-left"></i></button>
        <h4 id="years" class="year"></h4>
        <button class="arrow-control" onclick="nextYears();"><i class="fas fa-chevron-right"></i></button>
    </div>
    <div class="list" id="yearlist"></div>
</div>

JS

// 資料
let state = null;
let options = {  year: 'numeric', month: 'long' } // 轉換月份為英文
let calendar_date = document.querySelector("#calendar_date");
let calendar_month = document.querySelector("#calendar_month");
let calendar_year = document.querySelector("#calendar_year");
let datepickerInput = document.querySelector("#datepickerInput");

// 初始化Calendar
function init() {
    state = {
        current: new Date()
    };
    render();
}
function preMonth() {
    state.current.setMonth(state.current.getMonth()-1);
    render();
}
function nextMonth() {
    state.current.setMonth(state.current.getMonth()+1);
    render();
}
function preYear() {
    state.current.setYear(state.current.getFullYear()-1);
    render();
}
function nextYear() {
    state.current.setYear(state.current.getFullYear()+1);
    render();
}
function preYears() {
    state.current.setYear(state.current.getFullYear()-10);
    render();
}
function nextYears() {
    state.current.setYear(state.current.getFullYear()+10);
    render();
}
function showMonth() {
    calendar_month.className = 'calendar';
    calendar_date.className = 'd-none'
}
function showYear() {
    calendar_year.className = 'calendar';
    calendar_month.className = 'd-none'
}
function renderWeek() {
    let week = document.querySelector("#week");

    cal_days_labels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    for (var i = 0; i <= 6; i++) {
        week.innerHTML += '<div class="day">'+ cal_days_labels[i] + '</div>';
    }
}
renderWeek();
// 根據資料產生畫面
function render() {
    let head = document.querySelector("#year-month");
    head.textContent = new Intl.DateTimeFormat('en-US', options).format(state.current);            
    // head.textContent = state.current.getFullYear() + " / " + (state.current.getMonth()+1);
    let list = document.querySelector("#list");
    list.innerHTML = ""; // 清空畫面

    // 取得這個月的第一天
    let firstDate = new Date(state.current.getFullYear(), state.current.getMonth(), 1);
    // 往前算到星期日
    let date = new Date(firstDate.getFullYear(), firstDate.getMonth(), 1);
    date.setDate(date.getDate() - date.getDay());
    // 畫出上個月的後幾天
    while( date < firstDate ){
        renderDate(date, list);
        date.setDate(date.getDate()+1);
    }
    // 畫出這個月的日期
    while( date.getMonth() === state.current.getMonth() ){
        // 畫出一天的格子
        renderDate(date, list);
        // 日期+1
        date.setDate(date.getDate()+1);
    }
    // 畫出下個月的前幾天
    while( date.getDay() > 0 ){
        renderDate(date, list);
        date.setDate(date.getDate()+1);
    }

    // 產生月份
    let year = document.querySelector("#year");
    year.textContent = state.current.getFullYear();
    let monlist = document.querySelector("#monlist");
    monlist.innerHTML = "";
    let mon = (state.current.getMonth()+1);
    renderMonth(mon, monlist);

    // 產生年
    let years = document.querySelector("#years");
    let currentYear = state.current.getFullYear();
    let order = currentYear % 10;
    let recentYear = currentYear - (order+1);
    years.textContent = ( recentYear +  " - " + (recentYear + 11) );
    let yearlist = document.querySelector("#yearlist");
    yearlist.innerHTML = "";            
    renderYears(years, yearlist);
}
function renderDate(date, list) {
    let cell = document.createElement("button");
    cell.className = "date" + (date.getMonth() === state.current.getMonth() ? "" : " fadeout");
    cell.setAttribute("onmousedown", "selectDate(this)");
    let month = (date.getMonth()+1);
    let day = date.getDate();
    if( month < 10 && day < 10 ) {
        month = '0' + month;
        day = '0' + day;
    }else if ( month < 10 ) {
        month = '0' + month;
    }else if ( day < 10 ) { 
        day = '0' + day;
    }
    cell.setAttribute("value", (date.getFullYear() + '-' + month + '-' + day) );            
    cell.textContent = date.getDate();
    list.appendChild(cell);
}
function renderMonth(mon, monlist) {
    months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    for (var i = 0; i <= 11; i++) {
        monlist.innerHTML += '<button class="mon" onclick="selectMonth(this);" value="'+ [i] + '">'+ months[i] + '</button>';
    }
}
function renderYears(years, yearlist) {
    let currentYear = state.current.getFullYear();
    let order = currentYear % 10;            
    let recentYear = currentYear - (order+1);

    for (var i = 0; i < 12; i++) {
        yearlist.innerHTML += '<button class="mon" onclick="selectYear(this);" value="'+ (recentYear+i) + '">'+ (recentYear+i) + '</button>';
    }
}

// show Calendar
// datepickerInput.addEventListener('focus', (event) => {
//     showCalendar();
// });

function showCalendar() {
    let today = new Date();
    let todayDate = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
    calendar_date.className = 'calendar';

    let els = document.getElementsByClassName("date");
    for(let i = 0; i < els.length; i++)
    {
        if(datepickerInput.value == els[i].value) {
            els[i].classList.add("active");
        }else {
            els[i].classList.remove("active");
        }
        if(todayDate == els[i].value) {
            els[i].classList.add("today");
        }
    }
}
function hideCalendar(){
    calendar_date.className = 'd-none';
}

function toggleCalendar(){
    if (calendar_date.className === "d-none") {
        showCalendar();
    } else {
        hideCalendar();
    }
}

function selectDate(o) {
    datepickerInput.value = o.value;
    calendar_date.className = 'd-none';
}
function selectMonth(o){
    state.current.setMonth(o.value);
    render();
    calendar_month.className = 'd-none';
    calendar_date.className = 'calendar';
}

function selectYear(o){
    state.current.setYear(o.value);
    render();            
    calendar_year.className = 'd-none';
    calendar_month.className = 'calendar';
}

function checkDate(o){
    let dateVal = o.value;
    let dateYear = dateVal.substr(0, 4);

    if ( dateVal.length == 8 ){                
        let dateMonth = dateVal.substr(4, 2);
        let dateDay = dateVal.substr(6, 2);
        if( dateDay > 31 ) {
            clearInput();
        } else {
            dateVal = dateYear + "-" + dateMonth + "-" + dateDay;
            o.value = dateVal;
            renderCalendar();
        }

    }else if(dateVal.length == 7) {
        let dateMonth = dateVal.substr(4, 1);
        let dateDay = dateVal.substr(5, 2);
        if( dateDay > 31 ) {
            clearInput();
        }else {
            dateVal = dateYear + "-0" + dateMonth + "-" + dateDay;
            o.value = dateVal;
            renderCalendar();
        }
    }
    function clearInput(){
        o.focus();
        o.value = '';
    }
    function renderCalendar(){
        let innerText = new Date(datepickerInput.value);
        state.current = innerText;
        hideCalendar();
        render();
    }

}



// 處理流程
init();

對了,最後面試沒上QQ


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Felix
iT邦研究生 2 級 ‧ 2021-11-29 23:34:58

加油!

我要留言

立即登入留言