<input type="date">在三个浏览器中弹出的日期框都不一样(下图依次谷歌,EDGE,火狐)
谷歌的比较质普,EDGE的像半成品,火狐的比较好看.
模仿火狐做个练习.
思路与体会:
1.将日期框分三行,第一行是年,月,今天. 第二行是周,像是一个表格标题头.第三行是六行七列的日(天)
2.建日期类.初始化年月日,一般是由INPUT框中传来,没有默认为当天的.类中保留INPTU的引用.其它需要生成年,月,日数据的方法和生成DOM的方法.最终由一个方法生成日期框全部的DOM.
3.六行七列的天数,起止点是由给定的年月计算,在这年月的1号往前推到最近的周日,这年月的最后一天后推到最近的周六.得出.
4.类方法放到INPUT的点击事件上弹出日期框,同时获得焦点.失去焦点时日期框消失.
5.日期计算并不太复杂,主要样式和脚本生成DOM费了工夫.出了一些BUG.主要是日期计算时,月份是0-11表示1-12月.
日期框:
样式模仿火狐版本的日期框
年份选项区范围1900-2100 手工输入(INPUT框)年份0-9999可识别
年份和月份不能在框上输入,只能选择.
不属于选定年月中的日,选中的日,今天.颜色上有区分
年份月份分别前进后退按钮
代码:
/* 日期类:将此函数作为日期框事件函数,传入this(日期框) 需要引用:JQ,JsExtFun.js */ function mydate(input) { // 初始化已选年月日 mydate.initDate(input); // 生成DOM var datedom = mydate.createDom(); // 根据日期框的位置显示日期DOM框 var thisleft = $(input).offset().left; var thistop = $(input).offset().top + $(input).outerHeight(); $(‘.mydatebox‘).remove(); // 显示新的日期框 $(‘body‘).append(String.DataBind(datedom, { left: thisleft, top: thistop })); $(‘.mydatebox‘).focus(); } // 触发日期框的INPUT的JQ对象引用 mydate.InputJQ; // 初始年 mydate.Year; // 初始月[0-11] mydate.Month; // 初始日[1-31] mydate.Day; // 初始化:已选年月,保存日期框的INPUT的JQ对象引用 mydate.initDate = function (input) { // 初始化选定年月日,如果没有,则默认今天 var date = new Date(); if (input.value.IsDate()) { var ymd = input.value.split(‘-‘); date = new Date(parseInt(ymd[0]), parseInt(ymd[1]) - 1, parseInt(ymd[2])); } mydate.Year = date.getFullYear(); mydate.Month = date.getMonth(); mydate.Day = date.getDate(); mydate.InputJQ = $(input); } // 生成整个日期框的DOM.并返回 mydate.createDom = function () { var box = ‘<div class="mydatebox" style="left:${left}px;top:${top}px" tabindex="-1" onblur="mydate.close()">{0}{1}{2}</div>‘; var yearrow = String.Format(‘<div class="yearrow">{0}{1}{2}</div>‘ , mydate.createDom_Year() , mydate.createDom_Month() , ‘<div class="todayarea"><a class="today" onclick="mydate.todayBtn_click(this)">今天</a></div>‘); var weekrow = String.Format(‘<div class="weekrow">{0}</div>‘ , mydate.createDom_Week()); var daysrows = String.Format(‘<div class="daysrows">{0}</div>‘ , mydate.createDom_Day()); return String.Format(box, yearrow, weekrow, daysrows); } // 生成年份区DOM片段.并返回. mydate.createDom_Year = function () { var box = ‘<div class="yeararea">${prevbtn}${yearbtn}${nextbtn}</div>‘; var data = {}; data.prevbtn = ‘<a class="prevbtn" onclick="mydate.prevnextYM_click(this,1,2)"><</a>‘; data.yearbtn = String.Format( ‘<b class="yearbtn" onclick="mydate.yearBtn_click(this)" val="{0}">{0}年</b>‘ , mydate.Year); data.nextbtn = ‘<a class="nextbtn" onclick="mydate.prevnextYM_click(this,1,1)">></a>‘; return box = String.DataBind(box, data); } // 年份选择项DOM片段.selectedYear:可指定一个年份为已选定 mydate.createDom_YearSelect = function (selectedYear) { var ydoms = ‘‘; var ylist = mydate.domYear_Data(); for (var i = 0; i < ylist.length; i++) { ydoms += String.Format(‘<b class="{0}" val="{1}" onclick="mydate.yearSelected(this)">{1}</b>‘, ylist[i] == selectedYear ? "selected" : "", ylist[i]); } return String.Format(‘<div class="yearsops">{0}</div>‘, ydoms); } // 生成月份区DOM片段.并返回. mydate.createDom_Month = function () { var box = ‘<div class="montharea">${prevbtn}${monthbtn}${nextbtn}</div>‘; var data = {}; data.prevbtn = ‘<a class="prevbtn" onclick="mydate.prevnextYM_click(this,2,2)"><</a>‘; data.monthbtn = String.Format(‘<b class="monthbtn" onclick="mydate.monthBtn_click(this)" val="{0}">{1}月</b>‘ ,mydate.Month, mydate.Month + 1); data.nextbtn = ‘<a class="nextbtn" onclick="mydate.prevnextYM_click(this,2,1)">></a>‘; return box = String.DataBind(box, data); } // 生成月份选择项DOM片段 selectedMonth:可指定一个月份为已选定 mydate.createDom_MonthSelect = function (selectedMonth) { var mdoms = ‘‘; for (var i = 0; i < 12; i++) { mdoms += String.Format( ‘<b class="{0}" onclick="mydate.monthSelected(this)" val="{1}">{2}</b>‘ , selectedMonth == i ? "selected" : ‘‘,i, i + 1); } return String.Format(‘<div class="monthsops">{0}</div>‘, mdoms); } // 生成星期标题头DOM版本.并返回 mydate.createDom_Week = function () { var weeksdom = ‘‘; var weeks = [‘日‘, ‘一‘, ‘二‘, ‘三‘, ‘四‘, ‘五‘, ‘六‘]; for (var i = 0; i < weeks.length; i++) { weeksdom += ‘<b>周‘ + weeks[i] + ‘</b>‘; } return weeksdom; } // 生成日选项DOM片段.并返回.daylist:日数据.不传则使用选定年月计算出日 mydate.createDom_Day = function (daylist) { var data = typeof daylist == ‘undefined‘ ? mydate.domDay_Data() : daylist; var daydoms = ‘‘; var index = 0; for (var i = 0; i < 6; i++) { var weeksdays = ‘‘; for (var j = 0; j < 7; j++) { var json = data[index]; var daydom = ‘<b class="${istoday} ${isdayinmonth} ${isselected}" year="${yyyy}" month="${MM}" day="${dd}" onclick="mydate.day_click(this)">${dd}</b>‘; json.istoday = json.istoday ? ‘today‘ : ‘‘; json.isselected = json.isselected ? ‘selected‘ : ‘‘; json.isdayinmonth = json.isdayinmonth ? ‘‘ : ‘dayoutmonth‘; weeksdays += String.DataBind(daydom, json); index++; } daydoms += String.Format(‘<div class="daysrow">{0}</div>‘, weeksdays); } return daydoms; } /* 为DOM提供的数据,年份 日 */ // 根据已选年计算年份选项 mydate.domYear_Data = function () { // 年份选择范围固定在[1900-2100] var data = []; for (var i = 1900; i < 2101; i++) { data.push(i); } return data; /**动态年份数据旧代码.**/ //// 年份范围[0-9999](7个,已选年在中间) //var ymid = mydate.Year; //if (typeof yearmiddle != ‘undefined‘) //{ // ymid = parseInt(yearmiddle); //} //if (ymid < 3) // ymid = 3; //else if (ymid > 9996) // ymid = 9996; //var data = []; //for (var i = -3; i < 4; i++) //{ // data.push(ymid + i); //} ////console.log(data); //return data; /****************************************************/ } // 根据已选年月或者传入指定年月,计算日的起始和结束 // 日(天)总共六行七列42个,含已选年月所有日, 前推至最近的周日, 后推至最近或次近的周六 mydate.domDay_Data = function (yyyy,mm) { // 指定年 var seledY = typeof yyyy == ‘undefined‘ ? mydate.Year : parseInt(yyyy); // 指定月 var seledM = typeof mm == ‘undefined‘ ? mydate.Month : parseInt(mm); // 指定年月的起止日(1~xx号) var firstdate = new Date(seledY, seledM, 1); var lastdate = new Date(seledY, seledM + 1, 0); // 根据日所在的星期0-6排列显示,确定1日和最后日是星期几 var week1day = firstdate.getDay(); //var weeklastday = lastdate.getDay(); // 1日往前推到最近的周日(起点),最后日推到最近的周六(终点) firstdate.setDate(firstdate.getDate() - week1day); //lastdate.setDate(lastdate.getDate() + (6 - weeklastday)); var todaystr = (new Date()).toLocaleDateString(); var daysrange = []; for (var i = 0; i < 42; i++) { var json = {}; json.yyyy = firstdate.getFullYear(); json.MM = firstdate.getMonth(); json.dd = firstdate.getDate(); // 日是否属于指定年月中的日 json.isdayinmonth = json.MM == seledM; // 日是否为今天 json.istoday = firstdate.toLocaleDateString() == todaystr; // 日是否选定(等于文本框中已选日) json.isselected = (json.yyyy == mydate.Year && json.MM==mydate.Month && json.dd == mydate.Day); firstdate.setDate(json.dd + 1); daysrange.push(json); } return daysrange; } /* 事件方法:年月的前进后退按钮,年月选择按钮,今天按钮 */ // 点击年按钮 显示年选择框 mydate.yearBtn_click = function (thisobj) { var seledY = $(thisobj).attr(‘val‘); var yearsops = $(thisobj).parent().find(‘.yearsops‘); if (yearsops.length == 1) { yearsops.remove(); return; } $(thisobj).parent().append(mydate.createDom_YearSelect(seledY)); // 定位已选年份到滚动框的中间(视口可见范围内) var yopsbox = $(thisobj).parent().find(‘.yearsops‘); var yseled = yopsbox.find(‘.selected‘); if (yseled.length == 0) yseled = yopsbox.find(‘[val=‘ + (new Date()).getFullYear() + ‘]‘); // 计算这个年份选项离父框的TOP值,然后滚动条滚动这个值-100(父框高(200)/2) var scrollval = yseled.position().top - 100; yopsbox.scrollTop(scrollval); } // 年选择框 上下翻页按钮1=上0=下 //mydate.yearTurn_click = function (thisobj, turntype) //{ // // 上翻:以第一个年份选项为中点,下翻以最后年份为中点 // var yops = $(thisobj).parent().find(‘b‘); // var ymiddle = turntype == 1 ? yops.first() : yops.last(); // var newyops = mydate.createDom_YearSelect(ymiddle.attr(‘val‘)); // // 替换新的年份选项 // $(thisobj).parent().html($(newyops).children()); //} // 选定一个年份 mydate.yearSelected = function (thisobj) { // 日期DOM最外层JQ var datebox = $(thisobj).closest(‘.mydatebox‘); // 所选年份值 var y = $(thisobj).attr(‘val‘); datebox.find(‘.yearrow .yearbtn‘).attr(‘val‘, y).html(y + ‘年‘); // 关闭年份选择框 $(thisobj).parent().remove(); // 刷新 日 var m = datebox.find(‘.yearrow .monthbtn‘).attr(‘val‘); mydate.resetDaysDom(y, m, datebox.find(‘.daysrows‘)); } // 点击月按钮 显示月选择框 mydate.monthBtn_click = function (thisobj) { var seledM = $(thisobj).attr(‘val‘); var monthsops = $(thisobj).parent().find(‘.monthsops‘); if (monthsops.length == 1) { monthsops.remove(); return; } $(thisobj).parent().append(mydate.createDom_MonthSelect(seledM)); } // 选定一个月份 mydate.monthSelected = function (thisobj) { // 日期DOM最外层JQ var datebox = $(thisobj).closest(‘.mydatebox‘); // 所选月份值 var m = parseInt($(thisobj).attr(‘val‘)); datebox.find(‘.yearrow .monthbtn‘).attr(‘val‘, m).html((m+1) + ‘月‘); // 关闭月份选择框 $(thisobj).parent().remove(); // 刷新 日 var y = datebox.find(‘.yearrow .yearbtn‘).attr(‘val‘); mydate.resetDaysDom(y, m, datebox.find(‘.daysrows‘)); } // 点击年份,月份的前进和后退按钮 yOrm:1=年按钮,2=月按钮. pOrn:1=前进,2=后退 mydate.prevnextYM_click = function (thisobj,yOrm,pOrn) { // 日期DOM最外层JQ var datebox = $(thisobj).closest(‘.mydatebox‘); var ybtn = datebox.find(‘.yearrow .yearbtn‘); var mbtn = datebox.find(‘.yearrow .monthbtn‘); var y = parseInt(ybtn.attr(‘val‘)); var m = parseInt(mbtn.attr(‘val‘)); // 计算并刷新年或月按钮值 年份前进后退值[1-9999] if (yOrm == 1) { y = pOrn == 1 ? y + 1 : y - 1; if (y < 1) y = 9999; else if (y > 9999) y = 1; } else if (yOrm == 2) { m = pOrn == 1 ? m + 1 : m - 1; if (m < 0) m = 11; else if (m > 11) m = 0; } ybtn.attr(‘val‘, y).html(y + ‘年‘); mbtn.attr(‘val‘, m).html((m + 1) + ‘月‘); // 刷新日 mydate.resetDaysDom(y, m, datebox.find(‘.daysrows‘)); } // 点击今天按钮 mydate.todayBtn_click = function (thisobj) { var today = new Date(); mydate.InputJQ.val(today.ToString(1)); mydate.close(); } // 点击日(天) mydate.day_click = function (thisobj) { var date = new Date($(thisobj).attr(‘year‘), $(thisobj).attr(‘month‘) , $(thisobj).attr(‘day‘) ); mydate.InputJQ.val(date.ToString(1)); mydate.close(); } // 根据选定的年,月刷新日(用于当在日期框上操作年,月等会改变年月的动作时) // yyyy:指定年,mm:指定月 daysdom:日的父级DOM的JQ对象(.daysrows) mydate.resetDaysDom = function (yyyy,mm,daysdombox) { // 计算出指定年月的日数据 var dayslist = mydate.domDay_Data(yyyy, mm); // 生成日DOM var daysrowdom = mydate.createDom_Day(dayslist); // 替换日DOM daysdombox.html(daysrowdom); } // 关闭日期框 mydate.close = function () { mydate.InputJQ = null; mydate.Year = null; mydate.Month = null; mydate.Day = null; $(‘.mydatebox‘).remove(); }
样式:
/*外框*/ .mydatebox { position:absolute; width: 308px; box-sizing: border-box; font-size: 0; text-align: center; cursor: default; border: 1px solid #ccc; padding: 5px 0 10px 0; box-shadow: 1px 1px 10px #eee; -moz-user-select: none; -ms-user-select: none; -webkit-user-select: none; user-select: none; outline:none; } /*第1行 含有年月及前进和今天按钮*/ .yearrow { box-sizing: border-box; padding: 5px; } /*年,月,今天 框*/ .yearrow .yeararea, .yearrow .montharea, .yearrow .todayarea { position: relative; display: inline-block; width: 40%; height: 24px; line-height: 24px; } .yearrow .yeararea { width: 45%; } .yearrow .montharea { width: 35%; } .yearrow .todayarea { width: 20%; } /*年,月,今天 按钮*/ .yearrow .yearbtn, .yearrow .monthbtn, .yearrow .today { display: inline-block; font-size: 14px; font-weight: 600; width: 50%; height: 100%; border: 1px solid #eee; border-radius: 4px; vertical-align: middle; cursor: pointer; color:#292929; } .yearrow .yearbtn:hover, .yearrow .monthbtn:hover, .yearrow .today:hover { background-color: #eee; } .yearrow .today { width: auto; padding: 0 5px; } /*年月,前进后退按钮*/ .yearrow .prevbtn, .yearrow .nextbtn { display: inline-block; font-weight: 600; font-size: 18px; color: #999; width: 18px; height: 100%; border: 1px solid #eee; border-radius: 4px; vertical-align: middle; } .yearrow .prevbtn:hover, .yearrow .nextbtn:hover { color: #292929; cursor: pointer; background-color: #eee; } /*年月 选择框*/ .yearrow .yearsops, .yearrow .monthsops { position: absolute; top: 26px; left: 0; right: 0; box-sizing:border-box; border: 1px solid #bbb; border-radius: 4px; width: 80%; margin: 0 auto; border-top: none; z-index:9999; background-color: #fff; } .yearrow .yearsops{ padding:5px; width: 90%; height:220px; overflow-x:hidden; overflow-y:scroll; } /*年份上下翻页按钮*/ /*.yearrow .yearup,.yearrow .yeardown{ position:absolute; font-size:24px; font-weight:600; width:20px;height:30px; right:5px; cursor:pointer; border-radius:4px; color:#999; } .yearrow .yearup:hover,.yearrow .yeardown:hover{ color:#292929; } .yearrow .yearup{ top:30%; } .yearrow .yeardown{ top:45%; }*/ /**/ .yearrow .yearsops b, .yearrow .monthsops b { font-size: 13px; font-weight: 500; display: inline-block; border-radius: 4px; height: 28px; line-height: 28px; border-bottom:1px solid #eee; } .yearrow .yearsops b:hover, .yearrow .monthsops b:hover { background-color: #eee; } .yearrow .yearsops b { width: 50%; } .yearrow .yearsops b.selected, .yearrow .monthsops b.selected { background-color: #0996f8; } .yearrow .monthsops b { width: 50%; } /*第2行,固定的星期*/ .weekrow b, .daysrow b { font-size: 13px; font-weight: 500; display: inline-block; width: 14.28571428%; height: 24px; line-height: 24px; padding: 4px 0; } .weekrow b:first-child, .weekrow b:last-child, .daysrow b:first-child, .daysrow b:last-child { color: red; } /*第3行 日*/ .daysrow b { border-radius: 4px; } .daysrow b.today { color:#fff; background-color: #0996f8; } .daysrow b.selected{ background-color: #ccc; } .daysrow b:not(.today):not(.selected):hover { background-color: #eee; } .daysrow .dayoutmonth { opacity: .3; }