日历

使用面向对象的 js 代码写法,写了一个日历,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<div>
单个时间<input type="text" id="startTime">
时间段<input type="text" id="longTime">
</div>
<div id="content">
</div>
<script>

(function (window){
function $(tag, parent) {
var children = [];
if (tag.indexOf('#') !== -1) {
children = document.getElementById(tag.slice(1));
} else if (tag.indexOf('.') !== -1) {
children = parent ? parent.getElementsByClassName(tag.slice(1)) : document.getElementsByClassName(tag.slice(1));
} else {
children = parent ? parent.getElementsByTagName(tag) : document.getElementsByTagName(tag);
}
return children;
}

function listenEvent(dom,event,fn) {

if (dom.attachEvent) {
dom.attachEvent('on' + event, fn);
} else {
dom.addEventListener(event, fn);
}
}

function dateUI (isSingle ,inputIdName, callBack) {
this.isSingle = isSingle;
this.inputIdName = inputIdName;
this.callBack = callBack;

var date = new Date();

this.data = {
weekDay: ['日', '一', '二', '三', '四', '五', '六'],
minDate: '',
maxData: '',
year: 2017,
month: 8,
selectDays: []
}
this.data.year = 2017;
this.data.month = 8;

}

dateUI.prototype = {
$ : function (tag, parent) {
var children = [];
if (tag.indexOf('#') !== -1) {
children = document.getElementById(tag.slice(1));
} else if (tag.indexOf('.') !== -1) {
children = parent ? parent.getElementsByClassName(tag.slice(1)) : document.getElementsByClassName(tag.slice(1));
} else {
children = parent ? parent.getElementsByTagName(tag) : document.getElementsByTagName(tag);
}
return children;
},
listenEvent: function (dom, event, fn) {
if (dom.attachEvent) {
dom.attachEvent('on' + event, fn);
} else {
dom.addEventListener(event, fn);
}
},
initTable: function () {
var that = this;
var title = document.createElement('div');
title.setAttribute('id', 'title');
title.innerHTML = '<span id="pre">back</span><input type="number" id="month"><input type="number" id="year"><span id="back">pre</span>'
var main = document.createElement('div');
main.setAttribute('id', 'main');
var table = document.createElement('table');
table.innerHTML = '<thead><tr></tr></thead><tbody></tbody>';
main.appendChild(table);
that.$('#content').appendChild(title);
that.$('#content').appendChild(main);
for (let i = 0; i < that.data.weekDay.length; i++) {
that.$('tr', that.$('thead')[0])[0].innerHTML += '<th>' + that.data.weekDay[i] + '</th>';
}
var _ROW = 6;
var _COL = 7;
for (var i = 0; i < _ROW; i++) {
that.$('tbody')[0].innerHTML += '<tr></tr>';
for (var j = 0; j < _COL; j++) {
that.$('tr', that.$('tbody')[0])[i].innerHTML += '<td></td>';
}
}
that.tds = document.getElementsByTagName('td');
that.tds = [].slice.call(that.tds);

},
reactData: function () {
var that = this;
var props = ['year', 'month'];
props.map(function (item) {
var dom = null;
item === 'year' ? dom = that.$('#year') : dom = that.$('#month');
Object.defineProperty(that.data, item, {
get: function () {
return dom.value; // 绑定dom, 自动获取到value值
},
set: function (newVal) {
if (item === 'month') {
if (newVal > 12) {
newVal = 1;
that.data.year++;
} else if (newVal <= 0) {
newVal = 12;
that.data.year--;
}
}
dom.value = newVal;
}
})
});
},
dateAct: function () {
var that = this;
var inputAdd = ['#month', '#year'];
that.listenEvent(that.$('#back') ,'click', function () {
that.data.month++;
that.initDate();
});
that.listenEvent(that.$('#pre') ,'click', function () {
that.data.month--;
that.initDate();
});
inputAdd.map(function (item) {
that.listenEvent($(item) ,'change', function () {
if (item === '#month') {
that.data.month = $(item).value;
} else {
that.data.year = $(item).value;
}
that.initDate();
})
});
},
initDate: function () {
var that = this;
var date = new Date(that.data.year, that.$('#month').value - 1, 1);
var startDate = date.getDay();
var allDates = new Date(that.data.year, that.$('#month').value, 0).getDate();
var tds = document.getElementsByTagName('td');
that.initColor();
var j = 0;
startDate = (startDate ? startDate : 7);
// startDate 表示这一个月份的一号是星期几,当是星期天的时候,要从下一行开始
for (let i = 0 ; i < 42; i++) {
if (i >= startDate) {
j >= allDates ? j = 1 : j++;
tds[i].innerHTML = j;
if(i < allDates + startDate ) {
tds[i].onclick = function () {
// 这里有bug


that.initColor(that.data.year, that.data.month, i);
that.initColor();
that.$('#' + that.inputIdName).value = '';
that.data.selectDays.map(function (item, index, array) {

that.$('#' + that.inputIdName).value = item.year + '-' + item.month + '-' + that.tds[item.day].innerHTML;
if (!that.isSingle) {
var min;
that.data.selectDays.map(function (item, index, array) {
if (item.day < that.data.selectDays[0].day) {
min = item;
} else {
min = that.data.selectDays[0];
}
}
);
}
});
that.callBack();
};
} else {
tds[i].onclick = null;
tds[i].className = 'notClick';
}
} else {
tds[i].className = 'notClick';
document.getElementsByTagName('td')[i].onclick = null; // 回收 click 事件
tds[i].innerHTML = new Date(that.data.year, that.$('#month').value - 1, 0).getDate() + 1 -startDate + i ;
}
}
},
initColor: function (year, month, day) {

var that = this;
var _minDate;
var _maxDate;
if (year) {
var obj = {};
obj.year = year;
obj.month = month;
obj.day = day;
if (that.isSingle) {
that.data.selectDays[0] = obj;
} else {
that.data.selectDays.push(obj);
if (that.data.selectDays.length >= 2) {
that.data.selectDays[1] = obj;
that.data.selectDays.length = 2;
}
}
} else {
that.tds.map(function (item) {
item.className = '';
});

that.data.selectDays.map(function (key) {

((key.year === that.data.year) && (key.month === that.data.month)) ? that.tds[key.day].className = 'tdActive' : '' ;
});

if (that.data.selectDays.length === 2) {
if (that.data.selectDays.length === 2) {
if (that.data.selectDays[0].day < that.data.selectDays[1].day) {
_minDate = that.data.selectDays[0].day;
_maxDate = that.data.selectDays[1].day;
} else {
_maxDate = that.data.selectDays[0].day;
_minDate = that.data.selectDays[1].day;
}
}
for (var j = _minDate + 1; j < _maxDate; j++) {
if (that.data.selectDays[0].month === that.data.selectDays[1].month && that.data.selectDays[0].month === that.data.month) {
that.tds[j].className = 'timeBar';
} else {
that.tds[j].className = '';
}
}
}
}
}

};
// 使用 $() 类似于 jQuery 中的调用写法,用来调用 dom 元素
// data.year // data.month 表示真实的年和月
// data 对象, 用来集中管理数据信息

// initTable 用于初始化表格,构建日期选择框


// initData 将data 内的数据和 input 输入框内的数据进行同步,
// 自动同步
if (window.dateCanlender !== 'undefined') {
listenEvent(document.getElementById('startTime'), 'click', function () {
$('#content').style.display = 'block'
});
var dateCanlender = new dateUI(true, 'startTime', function () {

})
dateCanlender.initTable();
dateCanlender.tds = document.getElementsByTagName('td');
dateCanlender.tds = [].slice.call(dateCanlender.tds);
dateCanlender.reactData();
var date = new Date();
dateCanlender.data.year = date.getFullYear();
dateCanlender.data.month = date.getMonth() + 1;

dateCanlender.initDate();
dateCanlender.dateAct();
}
}(window))
</script>
<style>
#content {
display: none;
border: 2px solid darkgray;
padding: 10px;
margin: 50px auto;
width: 300px;
}
#title {
margin: 10px 0;
display: flex;
justify-content: space-between;
}
#title span, #title input {
display: inline-block;
width: 25%;
text-align: center;
flex: 1;
}
#title input {
margin: 0 10px;
}
#main {
width: 300px;
}
table, table thead {
width: 100%;
}
table thead tr {
width: 100%;
margin: 10px 0;
/*
display: flex;
justify-content: space-between;
*/
}
table td {
text-align: center;
}
.tdActive {
color: white;
background-color: skyblue;
}

.timeBar {
background-color: gray;
}
.notClick{
color: lightgray;
}
</style>
</body>
</html>

思路

这段代码是使用面向对象的语法写的,面向对象程序设计的主要使用了构造函数和原型的语法。代码如下:
1
2
3
4
5
function person () {
}
person.prototype.eye = 2;
var he = new person();
he.eye // 2
上面的代码中,通过使用 new 操作符创建了 person 构造函数的一个实例,在构造函数中定义了一个属性 eye ,根据在原型链中的继承的特性,使用 new 操作符创建的实例,会继承构造函数上定义的属性和方法,因此,我们可以在构造函数上定义公共属性和方法。
在通过 new 操作符的过程中发生了什么?1.创建一个新对象2.将构造函数的作用域赋给新对象,(这时候 this 指向这个新对象)3.执行构造函数的代码 (这个新对象添加属性)4.返回新对象
在上面的代码中
1
2
3
var dateCanlender = new dateUI(true, 'startTime', function () {

})
这段代码创建了构造函数 dateUI 的一个新实例。在构造函数的原型上,给原型添加方法 : initTable reactData initDate initColor dateActinitTable 用来初始化表格,生成表格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
initTable: function () {
var that = this;
var title = document.createElement('div');
title.setAttribute('id', 'title');
title.innerHTML = '<span id="pre">back</span><input type="number" id="month"><input type="number" id="year"><span id="back">pre</span>'
var main = document.createElement('div');
main.setAttribute('id', 'main');
var table = document.createElement('table');
table.innerHTML = '<thead><tr></tr></thead><tbody></tbody>';
main.appendChild(table);
that.$('#content').appendChild(title);
that.$('#content').appendChild(main);
for (let i = 0; i < that.data.weekDay.length; i++) {
that.$('tr', that.$('thead')[0])[0].innerHTML += '<th>' + that.data.weekDay[i] + '</th>';
}
var _ROW = 6;
var _COL = 7;
for (var i = 0; i < _ROW; i++) {
that.$('tbody')[0].innerHTML += '<tr></tr>';
for (var j = 0; j < _COL; j++) {
that.$('tr', that.$('tbody')[0])[i].innerHTML += '<td></td>';
}
}
that.tds = document.getElementsByTagName('td');
that.tds = [].slice.call(that.tds);
上面中的代码 [].slice.call(that.tds) 将类数组对象 tds 转化为数组,从而可以使用数组的方法
数组中 slice 的方法
reactData: 将数据变为可以跟踪变化的形式,类似于在 vue 中的,将 data 数据变为可响应式的,使用的是 Object.defineProperty() 方法, getter setter 等方法构建响应式数据。通过这种方法创建的数据是可以响应式的。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
reactData: function () {
var that = this;
var props = ['year', 'month'];
props.map(function (item) {
var dom = null;
item === 'year' ? dom = that.$('#year') : dom = that.$('#month');
Object.defineProperty(that.data, item, {
get: function () {
return dom.value; // 绑定dom, 自动获取到value值
},
set: function (newVal) {
if (item === 'month') {
if (newVal > 12) {
newVal = 1;
that.data.year++;
} else if (newVal <= 0) {
newVal = 12;
that.data.year--;
}
}
dom.value = newVal;
}
})
});
},
initDate 用来像表格中填入数据:代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
initDate: function () {
// 将 this 值存入 that,为了防止在 onclick 事件中 this 值改变的情况
var that = this;
var date = new Date(that.data.year, that.$('#month').value - 1, 1);
// startDate 得到的是当月 1 号的星期几
var startDate = date.getDay();
// allDates 得到的是当前月全部的日期
var allDates = new Date(that.data.year, that.$('#month').value, 0).getDate();
var tds = document.getElementsByTagName('td');
that.initColor();
var j = 0;
startDate = (startDate ? startDate : 7);
// startDate 表示这一个月份的一号是星期几,当是星期天的时候,要从下一行开始
// 下面的程序用来向表格内填入天的号数
for (let i = 0 ; i < 42; i++) {
// 判断开始时间,即判断表格中位于 1 号之前的表格
if (i >= startDate) {
j >= allDates ? j = 1 : j++;
tds[i].innerHTML = j;
// 定义位于中间的表格可以点击区域
if(i < allDates + startDate ) {
tds[i].onclick = function () {
// 将当前的年 月 表格输存入数组
that.initColor(that.data.year, that.data.month, i);
that.initColor();
// 清空input 框内的数据
that.$('#' + that.inputIdName).value = '';
that.data.selectDays.map(function (item, index, array) {
that.$('#' + that.inputIdName).value = item.year + '-' + item.month + '-' + that.tds[item.day].innerHTML;
if (!that.isSingle) {
var min;
that.data.selectDays.map(function (item, index, array) {
if (item.day < that.data.selectDays[0].day) {
min = item;
} else {
min = that.data.selectDays[0];
}
}
);
}
});
// 点击事件完成之后调用回调函数
that.callBack();
};
} else {
// 使用 onclick = null 回收点击事件
tds[i].onclick = null;
tds[i].className = 'notClick';
}
} else {
tds[i].className = 'notClick';
document.getElementsByTagName('td')[i].onclick = null; // 回收 click 事件
tds[i].innerHTML = new Date(that.data.year, that.$('#month').value - 1, 0).getDate() + 1 -startDate + i ;
}
}
},
在上段代码中使用的日期对象的一些方法;initColor :用来重置表格的颜色dateAct 用来定义日历的一些行为

代码总结:

1.使用面向对象的写法进行编写的,而非之前的面向过程编写2.熟悉 js 中关于使用日期事件的一些方法

在代码中的一些问题:

1.代码逻辑混乱,这一部分需要加强2.代码中需要规范,包括注释,变量的定义3.使用面向对象的写法的时候,需要知道使用 使用 property 构建函数的时候发生了什么,以及代码中 this 的指向值

little Tips

  1. addEventListener attachEvent
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <div id='btn'>
    </div>
    <script>
    window.onload = function () {
    var button = document.getElementById('btn');
    Good.addEventListener('click', function () {
    console.log(this); // this 指向的是 id 为 good 的 dom 元素
    })
    }
    </script>
    addEventistener 用于监听事件target.addEventListenter(event, fn , [userCapture])event : 表示要进行监听的事件 [string]fn :表示 监听到事件之后的回调函数userCapture : 表示事件是应用到冒泡阶段还是应用到捕获阶段 [boolean]
    addEventListener中定义的 this 值指向的是 tartget 对象在 IE 下 使用 attachEvent 来实现类似 addEventListener 的行为:实现兼容性:
    1
    2
    3
    4
    5
    6
    7
    8
    function listenEvent(dom,event,fn) {

    if (dom.attachEvent) {
    dom.attachEvent('on' + event, fn);
    } else {
    dom.addEventListener(event, fn);
    }
    }
2.使用 Date对象对于得到月份的天数以及当前月第一天的星期几的求法是不同的