溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

使用JavaScript怎么編寫(xiě)一個(gè)日歷組件

發(fā)布時(shí)間:2021-04-12 17:09:48 來(lái)源:億速云 閱讀:258 作者:Leah 欄目:web開(kāi)發(fā)

這篇文章給大家介紹使用JavaScript怎么編寫(xiě)一個(gè)日歷組件,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

想要實(shí)現(xiàn)的效果

  • 點(diǎn)擊日期選擇框出現(xiàn)日歷

  • 有個(gè)日期控制欄幫助選擇日期, 包括年、月、日的選擇和今天

  • 日歷格子,初次點(diǎn)擊日期選擇框時(shí)顯示此刻的日期,日歷格子的日期應(yīng)該包括這個(gè)月的所有天數(shù),同時(shí)如果當(dāng)月的1號(hào)不是周日,還應(yīng)補(bǔ)全從周日到1號(hào)的天數(shù)。還要在這個(gè)月最后1號(hào)的后面補(bǔ)全到周六。

  • 日期控制欄和日歷格子的日期還有選擇框里的日期的變化要是同步的。

實(shí)現(xiàn)思路

為了組件的可復(fù)用性,需要用面向?qū)ο蟮乃枷搿?br/>

每個(gè)日歷組件都是一個(gè)日歷對(duì)象,主要包括日期選擇框,日期控制顯示欄,還有日歷格子,為了保持日期控制顯示欄和日歷格子日期同步變化,日期控制欄和日歷里面的每個(gè)格子都應(yīng)該包含一個(gè)Date屬性,點(diǎn)擊日歷里的格子,將格子存的Date屬性作為函數(shù)參數(shù),調(diào)用函數(shù)改變?nèi)掌诳刂茩陲@示的時(shí)間。同理,日期控制欄時(shí)間變化時(shí),也將Date屬性作為參數(shù)調(diào)用函數(shù),函數(shù)重新繪制日歷格子。

上碼:

function Calendar(parentId) {
  this.parentElement = document.getElementById(parentId);
  this.init();
}
Calendar.prototype = {
  init: function() {
    this.contains = document.createElement("div");
    this.contains.onselectstart = function(){return false};  //讓按鈕點(diǎn)擊時(shí)不會(huì)出現(xiàn)文字被選中的藍(lán)色塊
    this.dateInput = document.createElement("input");
    this.datePicker = document.createElement("div");
    this.showDateBar = document.createElement("div");
    this.dateBox = document.createElement("div");
    this.icon = document.createElement("i");
    this.contains.className = 'datepicker-container';
    this.dateInput.className = 'date-input';
    this.dateInput.readOnly = true;
    var parent = this;
    this.dateInput.onclick = function(event){
      parent.onDateInputClick(event);      //點(diǎn)擊日期選擇框時(shí)顯示日歷格子        
    };
    this.contains.onblur = function(){
      parent.datePicker.style.display = 'none';
    }
    this.datePicker.className = 'date-picker';
    this.datePicker.style.display = 'none';
    this.showDateBar.className = 'show-date';
    this.dateBox.className = 'date-box';
    this.icon.className = 'date-icon';
    this.icon.innerHTML = ''; //iconfont這里用的阿里圖標(biāo),可以自行替換
    this.datePicker.appendChild(this.showDateBar);
    this.datePicker.appendChild(this.dateBox);
    this.contains.appendChild(this.dateInput);
    this.contains.appendChild(this.icon);
    this.contains.appendChild(this.datePicker);
    this.parentElement.appendChild(this.contains); 
  },
}

初始化日期控制欄:

drawShowDateBar: function(parentElement){
    var parent = this;
    var nowDate = new Date();
    parentElement.date = nowDate;
    var nowYear = nowDate.getFullYear();
    var nowMonth = nowDate.getMonth();
    var nowDay = nowDate.getDate();
    //showDateBar內(nèi)容拼接
    var contentStr ='<div class="year-input"><span>'+nowYear+'年</span><i class="select-year-btn"></i><ul class="year-select-box" >';
    for(var i=0;i<150;i++){
      contentStr+='<li>'+(i+1900)+'年</li>';
    }
    contentStr+='</ul></div>'
          +'<div class="month-input"><i class="prev-month"></i><select class="months-options">'
    for(var i=0;i<12;i++){
      contentStr+='<option>'+(i+1)+'月</option>';
    }
    contentStr+='</select><i class="next-month"></i></div>'
          +'<div class="day-input"><i class="prev-day"></i><select class="days-options"></select>'
          +'<i class="next-day"></i></div>'
          +'<button class="today-btn">今天</button>'
          +'<div class="days-title">';
    var weekday = ['日', '一', '二', '三', '四', '五', '六'];
    for (var i = 0; i < 7; i++) {
      contentStr+='<span class="day-title">'+weekday[i]+'</span>';
    }
    contentStr+='</div>';
    parentElement.innerHTML = contentStr;
    this.changeShowDateBar(nowDate);  //插入到showTimeBar之后,初始化,傳入的參數(shù)是現(xiàn)在的時(shí)間
    var yearInput = parentElement.firstChild;
    //年選擇框點(diǎn)擊顯示和隱藏選擇列表
    yearInput.onclick = function(){   //target和this的區(qū)別 target是觸發(fā)事件的元素,this是處理事件的元素 
      var ul = this.lastChild;
      ul.style.display==='none'||ul.style.display==='none'? ul.style.display='inline-block':ul.style.display='none';
    };
    //為年選擇下拉框綁定點(diǎn)擊事件
    var yearSelectBox = yearInput.lastChild;
    var yearLi = yearSelectBox.children;
    for(var i=0;i<yearLi.length;i++){
      yearLi[i].onclick = function(){
        parent.showDateBar.date.setFullYear(this.innerText.slice(0,-1));
        parent.changeShowDateBar(parent.showDateBar.date);  //時(shí)間改變之后都要重新調(diào)用,因?yàn)椴煌?,不同月,某個(gè)月的天數(shù)不全一樣
      };
    }
    //為month的前后按鈕添加點(diǎn)擊事件
    var monthInput = yearInput.nextSibling;
    monthInput.firstChild.onclick = function(){
      var monthOptions = this.nextSibling;
        if(monthOptions.selectedIndex>0){
          parent.showDateBar.date.setMonth(--monthOptions.selectedIndex);
        }else{
          monthOptions.selectedIndex = 11;
          parent.showDateBar.date.setFullYear(parent.showDateBar.date.getFullYear()-1);
          parent.showDateBar.date.setMonth(11);
        }
      parent.changeShowDateBar(parent.showDateBar.date);
    };
    monthInput.lastChild.onclick = function(){
      var monthOptions = this.previousSibling;
      if(monthOptions.selectedIndex<11){
        parent.showDateBar.date.setMonth(++monthOptions.selectedIndex);
      }else{
        monthOptions.selectedIndex = 0;
        parent.showDateBar.date.setFullYear(parent.showDateBar.date.getFullYear()+1);
        parent.showDateBar.date.setMonth(0);
      }
      parent.changeShowDateBar(parent.showDateBar.date);
      
    }
    monthInput.children[1].onchange = function(){
      parent.showDateBar.date.setMonth(this.selectedIndex);
      parent.changeShowDateBar(parent.showDateBar.date)
    };

    //為day的前后按鈕添加點(diǎn)擊事件
    var dayInput = monthInput.nextSibling;
    dayInput.firstChild.onclick = function(){
      var dayOptions = this.nextSibling;
      if(dayOptions.selectedIndex>0){
        parent.showDateBar.date.setDate(dayOptions.selectedIndex--);
      }else{
        parent.showDateBar.date.setMonth(parent.showDateBar.date.getMonth()-1);
        parent.showDateBar.date.setDate(parent.getDaysOfMonth(parent.showDateBar.date));
      }
      parent.changeShowDateBar(parent.showDateBar.date);
    };
    dayInput.lastChild.onclick = function(){
      var dayOptions = this.previousSibling;
      if(dayOptions.selectedIndex < dayOptions.length-1){
        dayOptions.selectedIndex++;
        parent.showDateBar.date.setDate(dayOptions.selectedIndex+1);  
      }else{
        parent.showDateBar.date.setDate(1);
        parent.showDateBar.date.setMonth(parent.showDateBar.date.getMonth()+1); 
      }
      parent.changeShowDateBar(parent.showDateBar.date);
    };
    dayInput.children[1].onchange = function(){
      parent.showDateBar.date.setDate(this.selectedIndex+1);
      parent.changeShowDateBar(parent.showDateBar.date)
    };
    //為今天按鈕綁定點(diǎn)擊事件
    var todayBtn = dayInput.nextSibling;
    todayBtn.onclick = function(){
      parent.drawPicker(new Date());
      parent.changeShowDateBar(new Date());
    }  
  },

drawShowDateBar函數(shù)為日期控制欄的年份、月份、和天的點(diǎn)擊按鈕設(shè)置了點(diǎn)擊事件處理函數(shù)。還有選擇下拉框變化的處理函數(shù)。

在日期控制欄初始化時(shí),或者改變showDateBar的Date時(shí),都會(huì)調(diào)用changeShowDateBar 函數(shù)。這個(gè)函數(shù)主要根據(jù)傳入的日期改變?nèi)掌诳刂茩凇叭铡毕吕瓩诘奶鞌?shù),因?yàn)槊總€(gè)月的天數(shù)不盡相同,所以要根據(jù)傳入的日期來(lái)改變。會(huì)計(jì)算出傳入的日期對(duì)應(yīng)的月份有多少天,使用getDaysOfMonth函數(shù)計(jì)算。

//計(jì)算一個(gè)月的天數(shù)
  getDaysOfMonth: function(primalDate) {
    var date = new Date(primalDate); //要新建一個(gè)對(duì)象,因?yàn)闀?huì)改變date
    var month = date.getMonth();
    var time = date.getTime();    //計(jì)算思路主要是month+1,相減除一天的毫秒數(shù)
    var newTime = date.setMonth(month + 1);
    return Math.ceil((newTime - time) / (24 * 60 * 60 * 1000));
  },
changeShowDateBar : function(date){
    var yearInput = this.showDateBar.firstChild;
    var monthInput = yearInput.nextSibling;
    var dayInput = monthInput.nextSibling;
    yearInput.firstChild.innerText = date.getFullYear()+'年';
    var monthsOptions = monthInput.firstChild.nextSibling;
    monthsOptions.selectedIndex = date.getMonth();
    var daysOptions = dayInput.firstChild.nextSibling;
    var days = this.getDaysOfMonth(date);
    var dayStr = '';
    for(var i=1;i<=days;i++){
      dayStr+='<option>'+i+'日</option>';
    }
    daysOptions.innerHTML = dayStr;
  // console.log(date.toLocaleDateString()+'changeShowDateBar');
    daysOptions.selectedIndex = date.getDate()-1;
    this.drawPicker(date);
  },

在日期控制欄的Date變化后,日歷格子的日期也應(yīng)該要改變,顯示的日期要和日期控制欄的保持一致。所以在changeShowDateBar函數(shù)結(jié)尾處調(diào)用drawPicker函數(shù),重新繪制日歷格子。

繪制日歷格子的思路

drawPicker函數(shù)要根據(jù)傳入的日期繪制日歷格子。

  • 首先計(jì)算傳入的日期月份的天數(shù)

  • 計(jì)算這個(gè)月1號(hào)是周幾 。利用Date對(duì)象的date.setDate(1) //將天設(shè)置為1號(hào) 。date.getDay() //得到這天是周幾

  • 如果1號(hào)不是周日,則補(bǔ)全周日到1號(hào)的天數(shù)??梢岳胦ldDate.setDate(-1) //設(shè)置日期為原來(lái)日期的上個(gè)月的最后一天。注意setDate是會(huì)改變當(dāng)前日期的,并不是返回新的日期。

  • 從1號(hào)到這個(gè)月最后一天循環(huán)。

  • 補(bǔ)全最后一天到周六的天數(shù)

drawPicker函數(shù):

drawPicker: function(primalDate) {
    var date = new Date(primalDate); //要新建一個(gè)對(duì)象,因?yàn)闀?huì)改變date
    var nowMonth = date.getMonth()+1;
    var nowDate = date.getDate();
    var spanContainer = [];
    var dateBox = this.dateBox;
    dateBox.innerHTML = '';
    var time = date.getTime();
    var days = this.getDaysOfMonth(date); //計(jì)算出這個(gè)月的天數(shù)
    date.setDate(1);            //將date的日期設(shè)置為1號(hào)
    var firstDay = date.getDay();     //知道這個(gè)月1號(hào)是星期幾
    for (var i = 0; i < firstDay; i++) {  //如果1號(hào)不是周日(一周的開(kāi)頭),則在1號(hào)之前要補(bǔ)全
      var tempDate = new Date(date);
      tempDate.setDate(i - firstDay + 1);
      var span = document.createElement("span");
      span.className = "unshow";
      spanContainer.push({span : span, date : tempDate});
    }
    for (var i = 1; i <= days; i++) {    //1號(hào)到這個(gè)月最后1天
      var span = document.createElement("span");
      span.className = 'show';
      spanContainer.push({span : span, date : new Date(date)});
      date.setDate(i + 1);
    }
    for (var i = date.getDay(); i <= 6; i++) { //在這個(gè)月最后一天后面補(bǔ)全
      var span = document.createElement("span");
      span.className = "unshow";
      spanContainer.push({span : span, date : new Date(date)});
      date.setDate(date.getDate()+1);
    }
    for(var i=0;i<spanContainer.length;i++){
      var spanBox = spanContainer[i];
      var span = spanBox.span;
      span.year = spanBox.date.getFullYear(); //為每個(gè)span元素添加表示時(shí)間的屬性
      span.month = spanBox.date.getMonth() + 1;
      span.date = spanBox.date.getDate();
      span.innerText = spanBox.date.getDate();
      if(span.date === nowDate&&span.month === nowMonth) //如果這個(gè)span的日期為與傳入的日期匹配,設(shè)置類(lèi)名為select
        span.className+=" select";
      var parent = this;
      span.onclick = function(){  //設(shè)置點(diǎn)擊事件
        var target = event.target;
        var selected = target.parentElement.getElementsByClassName("select");
        for(var i=0 ;i<selected.length;i++){
          selected[i].className = selected[i].className.replace(" select","");
        };
        target.className+=" select";
        parent.changeDate(target.year, target.month, target.date); 
        parent.changeShowDateBar(new Date(target.year, target.month-1, target.date));  
      };
      dateBox.appendChild(span); //將span添加到dateBox中
    }
    this.changeDate(primalDate.getFullYear(), primalDate.getMonth()+1, primalDate.getDate())
    return;
  },
//日期框點(diǎn)擊時(shí)顯示日歷
  onDateInputClick: function(event) { 
    var target = event.target;
    var value = target.value;
    var datePicker = this.datePicker;
    if(datePicker.style.display==='none'){  //這里必須要在js文件里將datePicker.style.display設(shè)置為none,如果是在css文件里設(shè)置為none,得到的display為""
      datePicker.style.display = 'block';
    }else{
      datePicker.style.display = 'none';
      return; 
    }
    if (!value) this.drawShowDateBar(this.showDateBar); //繪制日歷的顯示欄 
  },
  changeDate : function(year, month, date){
    this.dateInput.value = year+"-"+(month<10?("0"+month):month)+"-"+(date<10?("0"+date):date);
  },

實(shí)現(xiàn)效果

使用JavaScript怎么編寫(xiě)一個(gè)日歷組件

有點(diǎn)丑......

實(shí)現(xiàn)中遇到的問(wèn)題

  • 日歷格子的繪制問(wèn)題 。要補(bǔ)全1號(hào)前面到周日的天數(shù),還要補(bǔ)全當(dāng)月最后1號(hào)到周六的天數(shù)。日歷格子的繪制可以分為3部分,當(dāng)月前面、當(dāng)月和當(dāng)月后面的。要計(jì)算出1號(hào)是周幾,然后將這周周日到1號(hào)的天數(shù)繪制。

  • 當(dāng)月的日歷從1號(hào)到最后1號(hào)循環(huán)繪制。補(bǔ)全最后1號(hào)到周六的天數(shù)(date.getDay()<=6)

  • 日歷格子和日期控制欄顯示的同步。在繪制時(shí)為每個(gè)日歷格子單元保存其代表的Date。點(diǎn)擊格子單元時(shí),調(diào)用changeShowDateBar函數(shù),將單元存的Date傳入,改變?nèi)掌诳刂茩陲@示的日期,然后重繪日歷格子。

  • 每個(gè)月天數(shù)不同,出現(xiàn)的“日”選擇框天數(shù)不同的問(wèn)題。在changeShowDateBar函數(shù)里會(huì)根據(jù)傳入的Date,計(jì)算當(dāng)月有多少天,然后動(dòng)態(tài)生成“日”選擇框應(yīng)有的天數(shù)。

  • 跨月,跨年的處理。在日期控制欄中,有月份和日的上下按鈕,在處理跨月和跨年時(shí),判斷這月(日)是否為最后一月(日),若為,則日期控制欄的Date的年(月)加1,將顯示的月(日)設(shè)為第一月(日),調(diào)用changeShowDateBar函數(shù)。同理判斷是否為第一月(日)。

用到的Date API

  • date.getFullYear() //得到date的年份

  • date.getMonth() //得到月份 0-11

  • date.getDate() //得到日期 1-31的數(shù)字

  • date.getDay() // 得到這天是周幾 0-6

  • date.getTime()// 得到date的時(shí)間戳 ms表示

  • date.setFullYear(2017); // 設(shè)置年份

  • date.setMonth(x) // 如果設(shè)置為0-11,則date為x年的1-12月,如果比11大,則會(huì)往前面推,會(huì)跳到x+([(n+1)/12])年的第(n+1)%12個(gè)月

  • 如果為負(fù)數(shù),例如-1則會(huì)調(diào)到上一年的最后一月去。

  • date.setDate(x) // 和setMonth是同理的,它會(huì)自動(dòng)根據(jù)當(dāng)月的天數(shù),判斷是否發(fā)生月份的變動(dòng)。-1代表date跳到上月的最后一天

  • date.setTime()// 根據(jù)時(shí)間戳設(shè)置date

關(guān)于使用JavaScript怎么編寫(xiě)一個(gè)日歷組件就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI