đôi khi trong 1 project bạn cần tạo ra 1 calendar vẽ bằng javascript và bạn không biết nên bắt đầu từ đâu. Hôm nay mình chia sẽ cách vẽ 1 calendar bằng javascript đơn giản.
tự vẽ một carlendar bằng javascript thuần
Bài này mình chỉ hướng dẫn bạn tạo 1 carlendar căn bản với Pure JavaScript (không sài thư viện), bạn có thể css thêm, hoặc viết các vấn đề sự kiện thậm chí kế thừa javascript các kiểu con đà điểu nhưng đó là bạn vì mình muốn viết 1 bài dành đơn giản ai đọc cũng sẽ dễ dàng hiểu và sử dụng được.
Yêu cầu
Đương nhiên bạn cần biết về javascript và chỉ thế thôi 😄 vì bài này mình muốn đơn giản nên không kế thừa hay prototype gì cả nên dễ đọc dễ hiểu mà
Vài chú ý trước khi vẽ calendar
Lưu ý để vẽ calendar js bạn cần quan tâm đến việc vẽ 1 dom html bằng js. Cú pháp:
/// tạo thẻ div
var div = document.createElement("div")
/// tạo class cho div
div.className = "class-name"
Mình sẽ dùng thẻ div để vẽ từng ô từng cell chứ không dùng thẻ table 😄 Nhưng nếu bạn thích thì bạn cứ dùng table.
hàm getDay javascript
Phương thức getDay() được dùng để lấy THỨ của một đối tượng ngày tháng.
(THỨ có giá trị 0, 1, 2, 3, 4, 5, 6 tương ứng với CN, T2, T3, T4, T5, T6, T7).
Cú pháp
đối_tượng_ngày_tháng.getDay();
/// mình muốn lấy thứ của ngày đầu tiên của tháng + năm
var first_day = (new Date(this.selectYear, this.selectMonth)).getDay()
Bắt Đầu vẽ Calendar javascript pure
nếu bạn đã có kiến thức về javascript thì đọc đoạn code sau bạn sẽ không hề thấy khó hiểu
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>tự viết calendar</title>
</head>
<body>
<div id="calendar"></div>
<script>
var today = new Date()
var currentMonth = today.getMonth()
var currentYear = today.getFullYear()
/// tạm thời chưa dùng tới nhưng cứ khởi tạo trước 2 biến LABEL_MONTH, LABEL_DATE
/// để hàm createBodyCalendar sẽ dùng sau này
const LABEL_MONTH = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
const LABEL_DATE = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
/// tạo 2 biến này để xác định tháng và năm mình sẽ lấy làm mốc để vẽ
var selectYear = currentYear
var selectMonth = currentMonth
//// bắt đầu vẽ calendar
drawCalendar(selectMonth, selectYear)
/// function này sẽ tạo dom calendar dựa vào month, year được truyền vào
function drawCalendar(month, year){
/// tạo thẻ cha bao calendar
var calendar = document.createElement("div")
calendar.className = 'calendar'
/// tạo header
var headCalendar = createHeaderCalendar(month, year)
/// thêm header calendar dom vào calendar chính
calendar.appendChild(headCalendar)
/// tạo thân calendar
var bodyCalendar = createBodyCalendar(month, year)
calendar.appendChild(bodyCalendar)
//// cuối cùng thêm vào dom html của page
document.getElementById("calendar").appendChild(calendar)
}
/**
* function này để tạo phần header cho calendar có cái nút next và prev
* để biết next là qua tháng nào năm nào chúng ta cần truyền vào biến month và year
*/
function createHeaderCalendar(month, year){
//// tạm thời return ra cái dom có cái text header
var header = document.createElement("div")
header.className = 'calendar__header'
header.innerText = 'đây là phần header calendar'
return header
}
/**
* function này để tạo phần cell cho calendar quan trọng nhất
* để biết chúng ta vẽ calendar cho tháng năm nào cần truyền vào biến month và year
*/
function createBodyCalendar(month, year){
//// tạm thời return ra cái dom có cái text header
var body = document.createElement("div")
body.className = 'calendar__body'
body.innerText = 'đây là phần body calendar'
return body
}
</script>
</body>
</html>
kết quả :
giải thích 1 chút:
drawCalendar
là hàm chạy chính, ban đầu truyền tháng hiện tại và năm hiện tại làm mặc định.- hàm
createHeaderCalendar
sẽ tạo header có nút next pre và hiện thị ngày tháng năm - hàm createBodyCalendar sẽ tạo phần quan trọng nhất của calendar, các cell sẽ được vẽ dạng dive nên nhớ thêm css nhé
Cập nhật hàm createBodyCalendar để có carlendar body
/**
* function này để tạo phần cell cho calendar quan trọng nhất
* để biết chúng ta vẽ calendar cho tháng năm nào cần truyền vào biến month và year
*/
function createBodyCalendar(month, year){
var body = document.createElement("div")
body.className = 'calendar__body'
// creating all cells
var dateLoop = 1;
// tạo cells, 1 tháng có 6 dòng
for (let i = 0; i < 6; i++) {
// tạo 1 div bọc các ngày trong 1 tuần lại ( nếu bạn vẽ bằng table thì đây là thẻ tr)
var row = document.createElement("div")
row.className = "calendar__row"
// trong 1 tuần luôn có 7 ngày ( nếu bạn vẽ bằng table thì đây là thẻ td )
for (let j = 0; j < 7; j++) {
/// hàm xác định thứ đầu tiên của tháng.
/// cái này rất quan trọng trong việc vẽ calendar
/// vì bạn cần xác định calendar này được vẽ vào thứ mấy sau đó bạn for cho hết tháng
var firstDay = (new Date(year, month)).getDay()
/// tạo cell ( ô )
var cell = document.createElement("div")
cell.className = "calendar__cell"
if (i === 0 && j < firstDay) {
/// ngày của tháng trước ( kiểu như disable đi )
cell.innerHTML = "-"
}
else if (dateLoop > daysInMonth(month, year)) {
//// ngày của tháng sau ( kiểu như disable đi )
cell.innerHTML = "-"
}
else {
cell.innerText = dateLoop
dateLoop++;
}
row.appendChild(cell)
}
// appending each row into calendar body.
body.appendChild(row)
}
return body
}
// check how many days in a month
function daysInMonth(iMonth, iYear) {
return 32 - new Date(iYear, iMonth, 32).getDate();
}
Cần Thêm css cho body
<style>
/* mình giới hạn chiều rộng calendar, bạn xóa max-width này đi nghen */
#calendar{
max-width: 500px;
}
/* css cho calendar */
.calendar{
display: block
}
.calendar__body{
display: block
}
.calendar__row{
display: flex;
}
.calendar__cell{
flex: 1;
padding: 10px;
background-color: #ccc;
border: 1px solid #f2f2f2;
}
</style>
kết quả:
Cập nhật hàm createHeaderCalendar để có carlendar header
/**
* function này để tạo phần header cho calendar có cái nút next và prev
* để biết next là qua tháng nào năm nào chúng ta cần truyền vào biến month và year
*/
function createHeaderCalendar(month, year){
//// tạm thời return ra cái dom có cái text header
var header = document.createElement("div")
header.className = 'calendar__header'
/// bắt đầu tạo phần nut next, pre
var headingTop = document.createElement("div")
headingTop.className = "calendar__header-top"
/// next
var prev = document.createElement("button")
prev.innerText = "prev"
prev.onclick = function(){
//// set lại cái biến selectMonth
/// bạn đang nghĩ chỉ cần trừ 1 lên nhưng cẩn thận
/// nếu tháng 12 đem cộng 1 thì ra tháng 13 😄
var indexDate = new Date(selectYear, month - 1, 1 )
selectMonth = indexDate.getMonth()
selectYear = indexDate.getFullYear()
/// cho vẽ lại với 2 giá trị mới
drawCalendar(selectMonth, selectYear)
}
headingTop.appendChild(prev)
/// year index
var yearIndex = document.createElement("span")
yearIndex.innerText = `Tháng ${selectMonth + 1} năm ${selectYear}`
headingTop.appendChild(yearIndex)
/// next
var next = document.createElement("button")
next.innerText = "next"
next.onclick = function(){
//// set lại cái biến selectMonth
/// bạn đang nghĩ chỉ cần cộng 1 lên nhưng cẩn thận
/// nếu tháng 12 đem cộng 1 thì ra tháng 13 😄
var indexDate = new Date(selectYear, month + 1, 1 )
selectMonth = indexDate.getMonth()
selectYear = indexDate.getFullYear()
/// cho vẽ lại với 2 giá trị mới
drawCalendar(selectMonth, selectYear)
}
headingTop.appendChild(next)
header.appendChild(headingTop)
/// bắt đầu tạo phần label thứ 2, thứ 3, thứ 4, thứ 5 ....
var titleDays = document.createElement("div")
titleDays.className = "calendar__row calendar__row-head"
for(var head = 0; head < LABEL_DATE.length; head++){
var thead = document.createElement("div")
thead.className = "calendar__cell calendar__cell-head"
thead.innerHTML = LABEL_DATE[head]
titleDays.appendChild(thead)
}
header.appendChild(titleDays)
/// kết thúc tạo phần label
return header
}
kết quả: nút next prev đã hoạt động:
Cập nhật thêm css cho heading
.calendar__header-top{ display: flex; justify-content: space-between; } .calendar__header-top button{ background-color: #0d6efd; color: #fff; border: 2px solid #0d6efd; text-decoration: null; white-space: normal; word-wrap: break-word; cursor: pointer; user-select: none; padding: .375rem .75rem; margin: .375rem; -webkit-border-radius: .125rem; border-radius: .125rem; }
Full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>tự viết calendar</title>
<style>
/* mình giới hạn chiều rộng calendar, bạn xóa max-width này đi nghen */
#calendar{
max-width: 500px;
}
/* css cho calendar */
.calendar{
display: block
}
.calendar__body{
display: block
}
.calendar__row{
display: flex;
}
.calendar__cell{
flex: 1;
padding: 10px;
background-color: #ccc;
border: 1px solid #f2f2f2;
}
.calendar__header-top{
display: flex;
justify-content: space-between;
}
.calendar__header-top button{
background-color: #0d6efd;
color: #fff;
border: 2px solid #0d6efd;
text-decoration: null;
white-space: normal;
word-wrap: break-word;
cursor: pointer;
user-select: none;
padding: .375rem .75rem;
margin: .375rem;
-webkit-border-radius: .125rem;
border-radius: .125rem;
}
</style>
</head>
<body>
<div id="calendar"></div>
<script>
var today = new Date()
var currentMonth = today.getMonth()
var currentYear = today.getFullYear()
/// tạm thời chưa dùng tới nhưng cứ khởi tạo trước 2 biến LABEL_MONTH, LABEL_DATE
/// để hàm createBodyCalendar sẽ dùng sau này
const LABEL_MONTH = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
const LABEL_DATE = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
/// tạo 2 biến này để xác định tháng và năm mình sẽ lấy làm mốc để vẽ
var selectYear = currentYear
var selectMonth = currentMonth
//// bắt đầu vẽ calendar
drawCalendar(selectMonth, selectYear)
/// function này sẽ tạo dom calendar dựa vào month, year được truyền vào
function drawCalendar(month, year){
/// tạo thẻ cha bao calendar
var calendar = document.createElement("div")
calendar.className = 'calendar'
/// tạo header
var headCalendar = createHeaderCalendar(month, year)
/// thêm header calendar dom vào calendar chính
calendar.appendChild(headCalendar)
/// tạo thân calendar
var bodyCalendar = createBodyCalendar(month, year)
calendar.appendChild(bodyCalendar)
//// cuối cùng thêm vào dom html của page
document.getElementById("calendar").innerHTML = ''
document.getElementById("calendar").appendChild(calendar)
}
/**
* function này để tạo phần header cho calendar có cái nút next và prev
* để biết next là qua tháng nào năm nào chúng ta cần truyền vào biến month và year
*/
function createHeaderCalendar(month, year){
//// tạm thời return ra cái dom có cái text header
var header = document.createElement("div")
header.className = 'calendar__header'
/// bắt đầu tạo phần nut next, pre
var headingTop = document.createElement("div")
headingTop.className = "calendar__header-top"
/// next
var prev = document.createElement("button")
prev.innerText = "prev"
prev.onclick = function(){
//// set lại cái biến selectMonth
/// bạn đang nghĩ chỉ cần trừ 1 lên nhưng cẩn thận
/// nếu tháng 12 đem cộng 1 thì ra tháng 13 😄
var indexDate = new Date(selectYear, month - 1, 1 )
selectMonth = indexDate.getMonth()
selectYear = indexDate.getFullYear()
/// cho vẽ lại với 2 giá trị mới
drawCalendar(selectMonth, selectYear)
}
headingTop.appendChild(prev)
/// year index
var yearIndex = document.createElement("span")
yearIndex.innerText = `Tháng ${selectMonth + 1} năm ${selectYear}`
headingTop.appendChild(yearIndex)
/// next
var next = document.createElement("button")
next.innerText = "next"
next.onclick = function(){
//// set lại cái biến selectMonth
/// bạn đang nghĩ chỉ cần cộng 1 lên nhưng cẩn thận
/// nếu tháng 12 đem cộng 1 thì ra tháng 13 😄
var indexDate = new Date(selectYear, month + 1, 1 )
selectMonth = indexDate.getMonth()
selectYear = indexDate.getFullYear()
/// cho vẽ lại với 2 giá trị mới
drawCalendar(selectMonth, selectYear)
}
headingTop.appendChild(next)
header.appendChild(headingTop)
/// bắt đầu tạo phần label thứ 2, thứ 3, thứ 4, thứ 5 ....
var titleDays = document.createElement("div")
titleDays.className = "calendar__row calendar__row-head"
for(var head = 0; head < LABEL_DATE.length; head++){
var thead = document.createElement("div")
thead.className = "calendar__cell calendar__cell-head"
thead.innerHTML = LABEL_DATE[head]
titleDays.appendChild(thead)
}
header.appendChild(titleDays)
/// kết thúc tạo phần label
return header
}
/**
* function này để tạo phần cell cho calendar quan trọng nhất
* để biết chúng ta vẽ calendar cho tháng năm nào cần truyền vào biến month và year
*/
function createBodyCalendar(month, year){
var body = document.createElement("div")
body.className = 'calendar__body'
// creating all cells
var dateLoop = 1;
// tạo cells, 1 tháng có 6 dòng
for (let i = 0; i < 6; i++) {
// tạo 1 div bọc các ngày trong 1 tuần lại ( nếu bạn vẽ bằng table thì đây là thẻ tr)
var row = document.createElement("div")
row.className = "calendar__row"
// trong 1 tuần luôn có 7 ngày ( nếu bạn vẽ bằng table thì đây là thẻ td )
for (let j = 0; j < 7; j++) {
/// hàm xác định thứ đầu tiên của tháng.
/// cái này rất quan trọng trong việc vẽ calendar
/// vì bạn cần xác định calendar này được vẽ vào thứ mấy sau đó bạn for cho hết tháng
var firstDay = (new Date(year, month)).getDay()
/// tạo cell ( ô )
var cell = document.createElement("div")
cell.className = "calendar__cell"
if (i === 0 && j < firstDay) {
/// ngày của tháng trước ( kiểu như disable đi )
cell.innerHTML = "-"
}
else if (dateLoop > daysInMonth(month, year)) {
//// ngày của tháng sau ( kiểu như disable đi )
cell.innerHTML = "-"
}
else {
cell.innerText = dateLoop
dateLoop++;
}
row.appendChild(cell)
}
// appending each row into calendar body.
body.appendChild(row)
}
return body
}
// check how many days in a month code from https://dzone.com/articles/determining-number-days-month
function daysInMonth(iMonth, iYear) {
return 32 - new Date(iYear, iMonth, 32).getDate();
}
</script>
</body>
</html>
Kết quả và ghi chú nếu muốn phát triển thêm
thành quả
ghi chú
trong function createBodyCalendar, các ngày của tháng trước chưa được tính toán ra, và tương tự ngày của tháng sau cũng chưa đc tính ra. bạn nên tính toán để show ra cái ngày đó để hoàn thiện hơn. nếu bạn muốn tìm hiểu kỹ hơn thì theo dõi phần 2 của việc tạo 1 calendar nha, vẽ calendar bằng javascript pure core phần 2 - Code optimization . Cảm ơn Bạn quan tâm bài viết