简陋到爆炸的动态日历(我都不好意思发出来)
效果图如下:(别介意,我知道很菜啦)
代码如下:
calendar.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>日历</title>
<link href="01.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="calendar" id="calendar"></div>
<script type="text/javascript" src="01.js"></script>
<!--必须要放在底部, 我也不知道为啥,目前。-->
</body>
</html>
style.css
/**
* 设置日历的大小
*/
.calendar{
width: 240px;
height: 400px;
display: block;
}
/**
* 设置日历顶部盒子
*/
.calendar .calendar-title-box{
position: relative;
width: 100%;
height: 36px;
line-height: 36px;
text-align:center;
border-bottom: 1px solid #ddd;
}
/**
* 设置上个月的按钮图标
*/
.calendar .prev-month {
position: absolute;
top: 12px;
left: 0px;
display: inline-block;
width: 0px;
height: 0px;
border-left: 0px;
border-top: 6px solid transparent;
border-right: 8px solid #999;
border-bottom: 6px solid transparent;
cursor: pointer;
}
/**
* 设置下个月的按钮图标
*/
.calendar .next-month {
position: absolute;
top: 12px;
right: 0px;
display: inline-block;
width: 0px;
height: 0px;
border-right: 0px;
border-top: 6px solid transparent;
border-left: 8px solid #999;
border-bottom: 6px solid transparent;
cursor: pointer;
}
/* 设置日历表格样式 */
.calendar-table{
width: 100%;
border-collapse: collapse;
text-align:center;
}
/* 表格行高 */
.calendar-table tr{
height: 30px;
line-height: 30px;
}
/* 当前天 颜色特殊显示 */
.currentDay {
color: red;
}
/* 本月 文字颜色 */
.currentMonth {
color: #999;
}
/* 其他月颜色 */
.otherMonth{
color: #ede;
}
style.js
(function(){
/*
* 用于记录日期,显示的时候,根据dateObj中的日期的年月显示
*/
var dateObj = (function(){
var _date = new Date(); // 默认为当前系统时间
return {
getDate : function(){//这是什么格式?
return _date;
},
setDate : function(date) {
_date = date;
}
};
})();
//(function(){})();是js内置的快速启动函数格式
// 设置calendar div中的html部分(函数的声明)
renderHtml();
// 表格中显示日期
showCalendarData();
// 绑定事件
bindEvent();
/**
* 渲染html结构
*/
function renderHtml() {
var calendar = document.getElementById("calendar");//返回的是第一个符合id的对象引用
var titleBox = document.createElement("div"); // 标题盒子 设置上一月 下一月 标题
var bodyBox = document.createElement("div"); // 表格区 显示数据
/*Document.createElement()用给定的标签名称创建一个新的元素。*/
// 设置标题盒子中的html
titleBox.className = 'calendar-title-box';
titleBox.innerHTML = "<span class='prev-month' id='prevMonth'></span>" +
"<span class='calendar-title' id='calendarTitle'></span>" +
"<span id='nextMonth' class='next-month'></span>";
calendar.appendChild(titleBox); // 添加到calendar div中
// 设置表格区的html结构
bodyBox.className = 'calendar-body-box';
var _headHtml = "<tr>" +
"<th>日</th>" +
"<th>一</th>" +
"<th>二</th>" +
"<th>三</th>" +
"<th>四</th>" +
"<th>五</th>" +
"<th>六</th>" +
"</tr>";
var _bodyHtml = "";//?空?
// 一个月最多31天,所以一个月最多占6行表格
for(var i = 0; i < 6; i++) {
_bodyHtml += "<tr>" +
"<td></td>" +
"<td></td>" +
"<td></td>" +
"<td></td>" +
"<td></td>" +
"<td></td>" +
"<td></td>" +
"</tr>";
}
bodyBox.innerHTML = "<table id='calendarTable' class='calendar-table'>" +
_headHtml + _bodyHtml +
"</table>";
// 添加到calendar div中
calendar.appendChild(bodyBox);
}
/**
* 表格中显示数据,并设置类名
*/
function showCalendarData() {
var _year = dateObj.getDate().getFullYear();
var _month = dateObj.getDate().getMonth() + 1;
var _dateStr = getDateStr(dateObj.getDate());
// 设置顶部标题栏中的 年、月信息
var calendarTitle = document.getElementById("calendarTitle");
var titleStr = _dateStr.substr(0, 4) + "年" + _dateStr.substr(4,2) + "月";
calendarTitle.innerText = titleStr;
// 设置表格中的日期数据
var _table = document.getElementById("calendarTable");
var _tds = _table.getElementsByTagName("td");
var _firstDay = new Date(_year, _month - 1, 1); // 当前月第一天
for(var i = 0; i < _tds.length; i++) {
var _thisDay = new Date(_year, _month - 1, i + 1 - _firstDay.getDay());
var _thisDayStr = getDateStr(_thisDay);
_tds[i].innerText = _thisDay.getDate();
//_tds[i].data = _thisDayStr;
_tds[i].setAttribute('data', _thisDayStr);
if(_thisDayStr == getDateStr(new Date())) { // 当前天
_tds[i].className = 'currentDay';
}else if(_thisDayStr.substr(0, 6) == getDateStr(_firstDay).substr(0, 6)) {
_tds[i].className = 'currentMonth'; // 当前月
}else { // 其他月
_tds[i].className = 'otherMonth';
}
}
}
/**
* 绑定上个月下个月事件
*/
function bindEvent() {
var prevMonth = document.getElementById("prevMonth");
var nextMonth = document.getElementById("nextMonth");
addEvent(prevMonth, 'click', toPrevMonth);
addEvent(nextMonth, 'click', toNextMonth);
}
/**
* 绑定事件
*/
function addEvent(dom, eType, func) {
if(dom.addEventListener) { // DOM 2.0
dom.addEventListener(eType, function(e){
func(e);
});
} else if(dom.attachEvent){ // IE5+
dom.attachEvent('on' + eType, function(e){
func(e);
});
} else { // DOM 0
dom['on' + eType] = function(e) {
func(e);
}
}
}
/**
* 点击上个月图标触发
*/
function toPrevMonth() {
var date = dateObj.getDate();
dateObj.setDate(new Date(date.getFullYear(), date.getMonth() - 1, 1));
showCalendarData();
}
/**
* 点击下个月图标触发
*/
function toNextMonth() {
var date = dateObj.getDate();
dateObj.setDate(new Date(date.getFullYear(), date.getMonth() + 1, 1));
showCalendarData();
}
/**
* 日期转化为字符串, 4位年+2位月+2位日
*/
function getDateStr(date) {
var _year = date.getFullYear();
var _month = date.getMonth() + 1; // 月从0开始计数
var _d = date.getDate();
_month = (_month > 9) ? ("" + _month) : ("0" + _month);
_d = (_d > 9) ? ("" + _d) : ("0" + _d);
return _year + _month + _d;
}
})();
关于js部分代码的解析图:
由于我几乎是会js零基础,所以花费了很多时间,但也对js有了点感悟。
js不仅仅是想css那样,以html为主体,它甚至可以独立为王。这才是最关键的,而如何实现js代码的自由,就要用到渲染。
这里主要是大家介绍下什么是渲染,以及浏览器是如何根据你的代码渲染html 和 css 文件内容,然后又是怎样显示到屏幕上的。
浏览器如何渲染html 和 css
因为有人已经写的很好了,所以我就不再造轮子了。
以下内容转自大佬博客:http://layout.imweb.io/article/how-to-render.html
在正式进入 CSS 布局之前,当然有必要先了解下浏览器是如何渲染 CSS 的。而 CSS 要发挥作用还得依赖于 HTML,所以我们来一起简单了解下浏览器如何渲染 HTML & CSS。
虽然具体渲染过程很复杂,但是还是可以将其分为几个关键路径,如下:
处理 HTML 标记并构建 DOM 树
处理 CSS 标记并构建 CSSOM 树
将 DOM 与 CSSOM 合并成一个渲染树
根据渲染树来布局,计算每个节点的几何信息,再将各个节点绘制到屏幕上
整体流程图如下:
构建 DOM 树
首先浏览器渲染页面前会根据 HTML 结构构建成对应的 DOM 树。
以下面的 HTML 代码为例:
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
</body></html>
其 DOM 树生成的流程如下图:
1.转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
2.令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“”、“”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
3.词法分析: 发出的令牌转换成定义其属性和规则的“对象”。
4.DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项,依此类推。
整个流程的最终输出就是我们这个简单页面的文档对象模型 (DOM),如下图:
CSSOM
在浏览器构建上面的 DOM 时,在文档的 head 部分遇到了一个 link 标记,该标记引用一个外部 CSS 样式表:style.css。由于预见到需要利用该资源来渲染页面,它会立即发出对该资源的请求,并返回以下内容:
style.css
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }
与处理 HTML 时一样,我们需要将收到的 CSS 规则转换成某种浏览器能够理解和处理的东西。因此,我们会重复 HTML 过程,不过是为 CSS 而不是 HTML:
CSS 字节转换成字符,接着转换成令牌和节点,最后挂靠到一个称为“CSS 对象模型”(CSSOM) 的树结构内:
CSSOM 为何具有树结构?这是因为浏览器为页面上的任何对象计算最后一组样式时,都会先从适用于该节点的最通用规则开始(例如,如果该节点是 body 元素的子项,则应用所有 body 样式),然后通过应用更具体的规则(即规则“向下级联”)以递归方式优化计算的样式。
以上面的 CSSOM 树为例进行更具体的阐述。span 标记内包含的任何置于 body 元素内的文本都将具有 16 像素字号,并且颜色为红色 — font-size 指令从 body 向下级联至 span。不过,如果某个 span 标记是某个段落 (p) 标记的子项,则其内容将不会显示。
合并渲染树
接下来就是将 DOM 树与 CSSOM 树合并形成渲染树。
渲染树会网罗网页上所有可见的 DOM 内容,以及每个节点的所有 CSSOM 样式信息。
注:渲染树只包含渲染网页所需的节点,如display: none;的元素是不会出现在渲染树种的。
布局及绘制
有了渲染树,我们就可以进入“布局”阶段。
到目前为止,我们计算了哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口内的确切位置和大小---这就是“布局”阶段,也称为“自动重排”。
为弄清每个对象在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。让我们考虑下面这样一个简单的实例:
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Critial Path: Hello world!</title>
</head>
<body>
<div style="width: 50%">
<div style="width: 50%">Hello world!</div>
</div>
</body></html>
以上网页的正文包含两个嵌套 div:第一个(父)div 将节点的显示尺寸设置为视口宽度的 50%;而里面内嵌的第二个 div 将其宽度设置为其父项的 50%,即视口宽度的 25%。如下图:
布局流程的输出是一个“盒模型”,它会精确地捕获每个元素在视口内的确切位置和尺寸:所有相对测量值都转换为屏幕上的绝对像素。
最后,既然我们知道了哪些节点可见、它们的计算样式以及几何信息,我们终于可以将这些信息传递给最后一个阶段:将渲染树中的每个节点转换成屏幕上的实际像素形成我们可见的页面。这一步通常称为“绘制”或“栅格化”。
注:执行渲染树构建、布局和绘制所需的时间将取决于文档大小、应用的样式,以及运行文档的设备:文档越大,浏览器需要完成的工作就越多;样式越复杂,绘制需要的时间就越长(例如,单色的绘制开销“较小”,而阴影的计算和渲染开销则要“大得多”)。
参考资料
关键渲染路径