zoukankan      html  css  js  c++  java
  • 一步步教你实现跨游览器的JS日历

    最终效果可参见左边栏的日历(注,一定要在非IE下才可以浏览到,因为我还没有完全突破博客园的脚本封锁。在非IE游览器中,原日历被我hack掉,换成我的日历,可点击箭头选择年份与月份,日期上有悬浮效果与点击变色效果!)

    在开始之前我们先复习一下javascript的Date对象,这是一个内置对象,它包含了一系列方法供我们操作。按功能可以分为以下三大类:

    获得时间方法:

    • getDate() 查看Date对象并返回日期
    • getDay() 返回星期几
    • getHours() 返回小时数
    • getMinutes() 返回分钟数
    • getMonth() 返回月份值
    • getSeconds() 返回秒数
    • getMilliseconds()返回毫秒值
    • getTime() 返回完整的时间
    • getYear() 返回年份
    • getFullYear()返回一个四位数表示的年份
    • getTimezoneOffset() 返回本地时间和GMT相差的分钟数

    注:用getYear()返回的数并不一定是4位的!处于1900年和1999年间的getYear()方法返回的只有两位数。在此之前的或是在此之后的年份返回的都是四位数的。getYear()方法不应该再使用了。推荐使用getFullYear方法。另,javascript也提系列基于世界时的时间设置函数,如 getUTCDate(),getUTCDay(),getUTCFullYear(),getUTCHours(),getUTCMilliSeconds(),getUTCMinutes(),getUTCMonth ()与getUTCSeconds()方法。

    1./********根据一个日期求得星期,如'2009-6-21' return 0(星期日)***********/
    2.var get_day = function (strDate){
    3.    var f = strDate.replace(/-/g,'/');
    4.    f = new Date(f).getDay();
    5.    return "星期"+"天一二三四五六".split('')[f]
    6.}
    7.alert(get_day('2009-7-25'))

    设置时间方法:

    • setDate() 改变Date对象的日期
    • setYear() 改变年份
    • setMonth() 改变月份
    • setHours() 改变小时数
    • setMinutes() 改变分钟数
    • setSeconds() 改变秒数
    • setTime() 改变完整的时间
    //判断是否是闰年,返回true或者false Date.prototype.isLeapYear=function(){   varyear=this.getFullYear();   return(0==year%4&&((year%100!=0)||(year%400==0))); } //根据日期返回该日期所在年的周数 Date.prototype.getWeekNum=function(){   vardat=newDate(this.getFullYear(),0,1);   varweek=dat.getDay();   week=(week==0?7:week);   vardays=this.calDateDistance(dat,"dd")+1;   return((days+6-this.getDay()-7+week)/7); }

    注,由于javascript是从0开始的,因此需要对月份进行操作时要加1 .

    参数 描述
    month 必需。一个表示月份的数值,该值介于 0(一月) ~ 11(十二月) 之间。
    day 可选。一个表示月的某一天的数值,该值介于 1 ~ 31 之间(以本地时间计)。在 EMCAScript 标准化之前,不支持该参数。
    1.var now = new Date()
    2.var currentMonth = now.getMonth() -1 //获得当前的月份
    3.var nextMonth = now.getMonth() //获得下一个月的月份
    1.//用javascript取得某一年的第一个星期一的日期 
    2.function get(year) {
    3.    var d = new Date(year, 1, 1);
    4.    var day = d.getDay(); //获取1月1号是星期几
    5.    d.setDate((8 - day) % 7 + 1);
    6.    return d;
    7.}
    01.//求前 n 天或者后 n 天的日期(用xxxx-xx-xx表示)
    02.var showdate = function(n){
    03.    var d = new Date();
    04.    d.setDate(d.getDate()+n);
    05.    //或者 d = d.getFullYear() + "-" +  (d.getMonth()+1) + "-" + d.getDate();
    06.    d = d.toLocaleDateString().replace(/[\u4e00-\u9fa5]/g,'-').replace(/-$/,'')    
    07.    return d;
    08.}
    09.alert("今天是:"+showdate(0));
    10.alert("昨天是:"+showdate(-1));
    11.alert("明天是:"+showdate(1));
    12.alert("10天前是:"+showdate(-10));
    13.alert("8天后是:"+showdate(8));
    1.//将2005-8-5转换成2005-08-05格式 
    2.var strDate = '2005-8-5'
    3.window.alert(strDate.replace(/\b(\w)\b/g, '0$1'));
    01.// 对Date的扩展,将 Date 转化为指定格式的String 
    02.// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 
    03.// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
    04.// 例子: 
    05.// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
    06.// (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18 
    07.Date.prototype.Format = function(fmt) { //@author: meizz 
    08.    var o = { 
    09.        "M+" : this.getMonth()+1,                 //月份 
    10.        "d+" : this.getDate(),                    //日 
    11.        "h+" : this.getHours(),                   //小时 
    12.        "m+" : this.getMinutes(),                 //分 
    13.        "s+" : this.getSeconds(),                 //秒 
    14.        "q+" : Math.floor((this.getMonth()+3)/3), //季度 
    15.        "S"  : this.getMilliseconds()             //毫秒 
    16.    }; 
    17.    if(/(y+)/.test(fmt)) 
    18.        fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); 
    19.    for(var k in o) 
    20.        if(new RegExp("("+ k +")").test(fmt)) 
    21.            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); 
    22.    return fmt; 
    23.}

    转换时间方法:

    • toGMTString() 把Date对象的日期(一个数值)转变成一个GMT时间字符串,返回类似下面的值:Weds,15 June l997 14:02:02 GMT(精确的格式依赖于计算机上所运行的操作系统而变)
    • toLocaleString() 把Date对象的日期(一个数值)转变成一个字符串,使用所在计算机上配置使用的特定日期格式
    • UTC() 使用Date UTC(年、月、日、时、分、秒),以自从1970年1月1日00:00:00(其中时、分、秒是可选的)以来的毫秒数的形式返回
    • toLocaleDateString() 方法可根据本地时间把 Date 对象的日期部分转换为字符串,并返回结果
    • toLocaleTimeString() 方法可根据本地时间把 Date 对象的时间部分转换为字符串,并返回结果

    仅显示当前月的日历

    javascript日历可以写得很简单,也可以弄得很复杂。作为起步,我们先实现当前月的显示,然后再一点点改进。(其实我就不明白,分明是一个个月显示的,为什么不叫月历,而叫日历?!)

     
     我们先观察左边这个样板,日历通常是分为三部分,最上方是年份与星期数,中间是按一定规则排列的天数,最下方是按钮,当然这幅图缺了这个。一般而言,难点就是中间部分。我们要确定当月的第一天是星期几,要与上方的星期数对应,然后一个接一个向下排。而放置天数的方法,一般都是塞在一个表格中,需要嵌套循环来分别生成tr与td。为此,我想到另一个方法,把天数放置a元素中,利用液体布局的方式让它们自然地从左到右从上到下排列在一个div中。这样做个好处,就是hover样式是css原生的,不需要我们通过js的mouseover事件来绑定。为了塞满div,并分行显示,我们需要指定a元素的display为block,这样它的宽与高才能生效,但这样一行只能有一个a元素,因此我们还得指定其向左浮动,并限制div的宽与高,这样元素就按照我们的想法排序了。为了防止元素溢出,我们同时得把其容器也浮动。
     

    现在的问题怎样设置这些元素的内容,日历上面的空间并不都有数字的。首先我们得知道当前月第一天是星期几,因为js会把星期几转化为我们日历的第一行的数字;然后的问题是这个月有几天。我们先做个测试,把数组填空了再说!

    01.window.onload = function(){
    02.    //*********************第一阶段,填空日期数组*******************
    03.    var now = new Date(),
    04.    date = now.getDate(),//当前天数
    05.    month = now.getMonth() + 1,//当前的月份
    06.    year = now.getFullYear(),//当前的年份
    07.    firstday = new Date(now.getFullYear(), month -1  ,1).getDay(), //求出当月的第一天是星期几
    08.    lastday = new Date(now.getFullYear(), month , 0).getDate(),//上月的第0天就是今月的最后一天
    09.    dates = lastday,//最后一天的号数就是这个月的天数
    10.    arr = new Array(42);//用来装载日期的数组,日期以‘xxxx-xx-xx’的形式表示
    11.    for(var i = 0,j = firstday; i < dates ; i ++ ,j ++){
    12.        arr[j] = year +'-'+ month +'-'+ (i+1) ;
    13.    }
    14.    /***********************以下是测试部分**********************/
    15.    for(var i = 0;i <42;i++){
    16.        document.write(arr[i]+' ');//打印测试结果
    17.    }
    18.}

    接着下来我们就可以绘制UI了,我们用一个DIV来代表日历本身,头部由填满一行的span表示(也就是把span的display设为block),紧接着是星期数,它们都是a元素,下面是日期数,也是a元素。我们需要区分a元素的样式,让表示星期数的带上一个class来特别设置。样式我们暂时放弃动态生成。

    01.<!doctype html>
    02.<html dir="ltr" lang="zh-CN">
    03.    <head>
    04.        <meta charset="utf-8"/>
    05.        <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    06.        <title>跨游览器的JS日历</title>
    07.        <style type="text/css">
    08.            #jcalendar {
    09.                210px;
    10.                background:#E0ECF9;
    11.                border:1px solid #479AC7;
    12.                float:left;
    13.            }
    14.            #jcalendar span {
    15.                float:left;
    16.                210px;
    17.                height:20px;
    18.                background:#479AC7;
    19.                color:#f90;
    20.                font-weight:bolder;
    21.                text-align:center;
    22.            }
    23.            #jcalendar .week {
    24.                background:#D5F3F4;
    25.                color:#808080;
    26.            }
    27.            #jcalendar .current{
    28.                background:#336699;
    29.                color:#fff;
    30.            }
    31.            #jcalendar a {
    32.                display:block;
    33.                float:left;
    34.                30px;
    35.                height:20px;
    36.                color:#000;
    37.                line-height:20px;
    38.                text-align:center;
    39.                text-decoration:none;
    40.            }
    41.  
    42.        </style>
    43.        <script type="text/javascript">
    44.        </script>
    45.    </head>
    46.    <body>
    47.    </body>
    48.</html>
    01.window.onload = function(){
    02.    //*********************第一阶段,填空日期数组*******************
    03.    //***************************略**************************
    04.    //**********************第二阶段,绘制UI******************
    05.    var body = document.getElementsByTagName('body')[0],//body的快捷引用
    06.    a = document.createElement('a'),//日历的a元素,用于克隆
    07.    calendar = document.createElement('div'),//日历的容器元素
    08.    thead = document.createElement('span');//日历的头部或页眉
    09.    body.insertBefore(calendar,null);//把日历加入DOM树中
    10.    calendar.setAttribute('id','jcalendar');
    11.    thead.innerHTML = year +"年  " + month +"月"
    12.    calendar.appendChild(thead);
    13.    var weeks = "日一二三四五六".split('');//日历第二行的内容,显示星期几
    14.    for(i = 0;i< 7;i++){
    15.        var th = a.cloneNode();
    16.        th.innerHTML = weeks[i];
    17.        th.className = 'week';
    18.        calendar.appendChild(th);
    19.    }
    20.    for(i = 0;i <42;i++){
    21.        var td = a.cloneNode();
    22.        if(arr[i] == undefined ){
    23.            calendar.appendChild(td);
    24.        }else{
    25.            var html = arr[i].split('-')[2];
    26.            td.innerHTML = html;
    27.            if(html == date){
    28.                td.className = 'current';
    29.            }
    30.            calendar.appendChild(td);
    31.        }
    32.    }
    33.}

    好了,静态的日历就做出来了,现在我们加入动态元素,在头部添加四个按钮,让它选择上一月,下一月,上一年与下一年。由于我们的日历是基于那个填空数组,而填空数组是是基于年份与月份,因此年份与月份一改变,我们就不可避免地重绘整个日历。我们把重绘日历的逻辑独立出来做成一个函数,好让按钮上的函数去调用它!并在日期上添加悬浮效果与单击事件。

    添加新样式

    01.#jcalendar .ybn {
    02.    color:#000;
    03.}
    04.#jcalendar .mbn {
    05.    color:#000040;
    06.}
    07.#jcalendar .weekend {
    08.    color:#f00!important;
    09.}
    10.#jcalendar a.day:hover{
    11.    background:#99C3F6;
    12.}

    javascript修改为:

    01.var fillArray = function(year,month){
    02.    var firstday = new Date(year, month -1  ,1).getDay(), //求出当月的第一天是星期几
    03.    lastday = new Date(year, month , 0).getDate(),//上个月的第零天就是今个月的最后一天
    04.    dates = lastday,//最后一天的号数就是这个月的天数
    05.    arr = new Array(42);//用来装载日期的数组,日期以‘xxxx-xx-xx’的形式表示
    06.    for(var i = 0,j = firstday; i < dates ; i ++ ,j ++){
    07.        arr[j] = year +'-'+ month +'-'+ (i+1) ;
    08.    }
    09.    return arr;
    10.}
    11.var nextmonth = function(year,month,date){//按钮事件1
    12.    month = month + 1;
    13.    if(month > 12)   year = year +1,month = 1;
    14.    var arr = fillArray(year,month);
    15.    drawCalendar(arr,year,month,date);
    16.}
    17.var nextyear = function(year,month,date){//按钮事件2
    18.    year = year + 1;
    19.    var arr = fillArray(year,month);
    20.    drawCalendar(arr,year,month,date);
    21.}
    22.var premonth = function(year,month,date){//按钮事件3
    23.    month = month - 1;
    24.    if(month < 1)  year = year -1,month =12;
    25.    var arr = fillArray(year,month);
    26.    drawCalendar(arr,year,month,date);
    27.}
    28.var preyear = function(year,month,date){//按钮事件4
    29.    year = year - 1;
    30.    var arr = fillArray(year,month);
    31.    drawCalendar(arr,year,month,date);
    32.}
    33.var drawCalendar = function(arr,year,month,date){
    34.    var _calendar = document.getElementById("jcalendar");
    35.    if(_calendar) _calendar.parentNode.removeChild(_calendar);
    36.   
    37.    var body = document.getElementsByTagName('body')[0],//body的快捷引用
    38.    a = document.createElement('a'),//日历的a元素,用于克隆
    39.    calendar = document.createElement('div'),//日历的容器元素
    40.    thead = document.createElement('span');//日历的头部或页眉
    41.    body.insertBefore(calendar,null);//把日历加入DOM树中
    42.    calendar.setAttribute('id','jcalendar');
    43.    var args = year+','+month+','+date,  
    44.    preyear = '<TT class=ybn onclick="preyear('+args+')"><<</TT>',
    45.    premonth = '<TT class=mbn onclick="premonth('+args+')"><</TT>',
    46.    nextmonth = '<TT class=mbn onclick="nextmonth('+args+')">></TT>',
    47.    nextyear = '<TT class=ybn onclick="nextyear('+args+')">>></TT>',
    48.    str = new Date(args.replace(/,/g,'/')).toLocaleDateString();
    49.    thead.innerHTML = preyear + ' '+premonth + ' '+str+' '+nextmonth + ' '+nextyear;
    50.    calendar.appendChild(thead);
    51.    var weeks = "日一二三四五六".split('');//日历第二行的内容,显示星期几
    52.    for(i = 0;i< 7;i++){
    53.        var th = a.cloneNode();
    54.        th.innerHTML = weeks[i];
    55.        th.className = 'week';
    56.        calendar.appendChild(th);
    57.    }
    58.    for(i = 0;i <42;i++){
    59.        var td = a.cloneNode();
    60.        if(arr[i] == undefined ){
    61.            calendar.appendChild(td);
    62.        }else{
    63.            var html = arr[i].split('-')[2];
    64.            td.innerHTML = html;
    65.            td.className = 'day';
    66.            td.href = "javascript:void(0)";//为ie6准备的
    67.            if(date && html == date){
    68.                td.className = td.className +' current';
    69.            }
    70.            if(i%7 == 0 || i%7 == 6){
    71.                td.className = td.className +' weekend';
    72.            }
    73.            td.onclick = (function(i){
    74.                return function(){
    75.                    alert(i);//这里后面我们要修改,让arr[i]填空文本域
    76.                }
    77.            })(arr[i]);
    78.            calendar.appendChild(td);
    79.        }
    80.    }
    81.}
    82.window.onload = function(){
    83.    //*********************第一阶段,填空日期数组*******************
    84.    var now = new Date(),
    85.    month = now.getMonth() + 1,//当前的月份
    86.    year = now.getFullYear(),//当前的年份
    87.    date = now.getDate(),
    88.    arr = fillArray(year,month);
    89.    //**********************第二阶段,绘制UI******************
    90.    drawCalendar(arr,year,month,date);
    91.}

    好了,该有的都有了,现在封装一下它吧。把上面四个按钮事件合并成一个,加个更见名达义的名称,然后统统塞到一个类中。

    001.var Class = {
    002.    create: function() {
    003.        return function() {
    004.            this.initialize.apply(this, arguments);
    005.        }
    006.    }
    007.}
    008.//*********************Jcalendar类开始**********************
    009.var Jcalendar =  Class.create();
    010.Jcalendar.prototype = {
    011.    initialize:function(){
    012.        var $ = new Date();
    013.        this.drawCalendar($.getFullYear(),$.getMonth() + 1,$.getDate());
    014.    },
    015.    fillArray : function(year,month){
    016.        var f = new Date(year, month -1  ,1).getDay(), //求出当月的第一天是星期几
    017.        dates = new Date(year, month , 0).getDate(),//上个月的第零天就是今个月的最后一天
    018.        arr = new Array(42);//用来装载日期的数组,日期以‘xxxx-xx-xx’的形式表示
    019.        for(var i = 0; i < dates ; i ++ ,f ++){
    020.            arr[f] = year +'-'+ month +'-'+ (i+1) ;
    021.        }
    022.        return arr;
    023.    },
    024.    drawCalendar : function(year,month,date){    
    025.        var $ = document,$$ = 'createElement',
    026.        _calendar = $.getElementById("jcalendar");
    027.        if(_calendar) _calendar.parentNode.removeChild(_calendar);//推倒重绘!
    028.        var body =  $.getElementsByTagName('body')[0],//body的快捷引用
    029.        weeks = "日一二三四五六".split(''),//日历第二行的内容,显示星期几
    030.        calendar = $[$$]('div'),//日历的容器元素
    031.        a =  $[$$]('a'),//日历的a元素,用于克隆
    032.        tt =  $[$$]("tt"),//日历页眉的tt元素,用于克隆
    033.        thead = $[$$]('span'),//日历页眉
    034.        fragment = $.createDocumentFragment(),//减少DOM刷新页面的次数
    035.        arr = this.fillArray(year,month),//保存当月的日期
    036.        tts = [],//用于保存tt元素的引用
    037.        ths = this;//用于保存Jcalendar对象的实例的引用
    038.        body.insertBefore(calendar,null);//把日历加入DOM树中
    039.        calendar.setAttribute('id','jcalendar');
    040.        for(var i = 0;i<4;i++){//循环生成出个时间按钮。
    041.            var clone = tt.cloneNode(true);//比重新createElement快
    042.            clone.onclick =  (function(index){
    043.                return function(){//在闭包里绑定事件
    044.                    ths.redrawCalendar(year,month,date,index)
    045.                }
    046.            })(i);
    047.            tts[i] = clone;//保存引用
    048.            if(i==2) thead.appendChild($.createTextNode(year+"年"+month+"月"+date+"日"));
    049.            thead.appendChild(clone);
    050.        }
    051.        tts[0].innerHTML = '<<';
    052.        tts[1].innerHTML = '<';
    053.        tts[2].innerHTML = '>';
    054.        tts[3].innerHTML = '>>';
    055.        tts[0].className = tts[3].className = 'mbn';
    056.        tts[1].className = tts[2].className = 'ybn';
    057.        fragment.appendChild(thead);     
    058.        for(i = 0;i <7;i++){//星期显示区
    059.            var th = a.cloneNode(true);
    060.            th.innerHTML = weeks[i];
    061.            th.className = 'week';
    062.            fragment.appendChild(th);
    063.        }
    064.        for(i = 0;i <42;i++){//日期显示区
    065.            var td = a.cloneNode(true);
    066.            if(arr[i] == undefined ){
    067.                fragment.appendChild(td);
    068.            }else{
    069.                var html = arr[i].split('-')[2];
    070.                td.innerHTML = html;
    071.                td.className = 'day';
    072.                td.href = "javascript:void(0)";//为ie6准备的
    073.                (date && html == date)&&(td.className += ' current') ;//高亮每个月今天这一天
    074.                (i%7 == 0 || i%7 == 6)&&(td.className += ' weekend') ;//为周末添加多一个类
    075.                td.onclick = (function(i){
    076.                    return function(){
    077.                        alert(i);
    078.                    }
    079.                })(arr[i]);
    080.                fragment.appendChild(td);
    081.            }
    082.        }
    083.        calendar.appendChild(fragment);
    084.    },
    085.  
    086.    redrawCalendar : function(year,month,date,index){
    087.        switch(index){
    088.            case 0 ://preyear
    089.                year--;
    090.                break;
    091.            case 1://premonth
    092.                month--;
    093.                (month < 1) &&(year--,month = 12)  ;
    094.                break;
    095.            case 2://nextmonth
    096.                month++;                
    097.                (month > 12)&&(year++,month = 1) ;
    098.                break;
    099.            case 3://nextyear
    100.                year++;
    101.                break;
    102.        }
    103.        this.drawCalendar(year,month,date);
    104.    }
    105.}
    106.//*********************Jcalendar类结束**********************
    107.window.onload = function(){
    108.        new Jcalendar();
    109.}

    上面的按钮的显示符号应该是&lt;与&gt;,而不是单纯的小于号或大于号,这是高亮插件的缘故……另,这个日历改一改,就可以成为弹出式日期选择器了!至于它,下次有次再说!

  • 相关阅读:
    MySQL忘记root密码的找回方法
    MySQL中的备份和恢复
    MySQL中的锁(表锁、行锁)
    设计模式之: 策略模式
    MySQL流程函数
    高效查看MySQL帮助文档的方法
    php动态获取函数参数
    设计模式之: 代理模式
    网站性能优化指标
    设计模式之: 状态模式
  • 原文地址:https://www.cnblogs.com/liufei88866/p/1537625.html
Copyright © 2011-2022 走看看