Chắc chắn có rất nhiều bạn sử dụng thư viện hoặc plugin để tạo datepicker như jQuery, Boostrap,… Nhưng bạn cũng hiểu được rằng chúng ta sẽ không dùng hết tính năng trong thư viện/plugin đó gây ra lãng phí tài nguyên. Vì vậy trong bài viết này sharescript sẽ hướng dẫn cách tạo datepicker chỉ sử dụng css và javascript.
Mình đã nén source và chia sẻ ở link download cuối bài viết, bạn có thể cuộn xuống dưới tải code về trước sau đó đọc bài viết để dễ hiểu hơn.
Tạo datepicker đơn giản

1) Tạo file index.html
<!DOCTYPE html> <html> <head> <title> Date Picker Options </title> <!-- (A) LOAD THE LIBRARIES --> <link href="dp-dark.css" rel="stylesheet"> <!-- <link href="dp-light.css" rel="stylesheet"> --> <script src="datepicker.js"></script> </head> <body> <!-- (B) THE HTML --> <h1>Date Picker Options</h1> <input type="text" id="input-opt"/> <div id="pick-opt"></div> <!-- (C) ATTACH DATE PICKER ON LOAD --> <script> window.addEventListener("load", function(){ picker.attach({ target: "input-opt", container: "pick-opt", disableday : [2, 7], // DISABLE TUE, SUN startmon: true // WEEK START ON MON }); }); </script> </body> </html>
2) Tạo file dp-dark.css
/* (A) POPUP */ .picker-wrap { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.5); opacity: 0; visibility: hidden; transition: opacity 0.2s; } .picker-wrap.show { opacity: 1; visibility: visible; } .picker-wrap .picker { margin: 50vh auto 0 auto; transform: translateY(-50%); } /* (B) CONTAINER */ .picker { max-width: 300px; border: 1px solid #000; background: #444; padding: 10px; } /* (C) MONTH + YEAR */ .picker-m, .picker-y { width: 50%; padding: 5px; box-sizing: border-box; font-size: 16px; } /* (D) DAY */ .picker-d table { color: #fff; border-collapse: separate; width: 100%; margin-top: 10px; } .picker-d table td { width: 14.28%; /* 7 EQUAL COLUMNS */ padding: 5px; text-align: center; } /* HEADER CELLS */ .picker-d-h td { font-weight: bold; } /* BLANK DATES */ .picker-d-b { background: #4e4e4e; } /* TODAY */ .picker-d-td { background: #d84f4f; } /* PICKABLE DATES */ .picker-d-d:hover { cursor: pointer; background: #a33c3c; } /* UNPICKABLE DATES */ .picker-d-dd { color: #888; background: #4e4e4e; }
3) Tạo file datepicker.js
var picker = { // (A) ATTACH DATEPICKER TO TARGET // target : datepicker will populate this field // container : datepicker will be generated in this container // startmon : start on Monday (default false) // disableday : array of days to disable, e.g. [2,7] to disable Tue and Sun attach : function (opt) { // (A1) CREATE NEW DATEPICKER var dp = document.createElement("div"); dp.dataset.target = opt.target; dp.dataset.startmon = opt.startmon ? "1" : "0"; dp.classList.add("picker"); if (opt.disableday) { dp.dataset.disableday = JSON.stringify(opt.disableday); } // (A2) DEFAULT TO CURRENT MONTH + YEAR - NOTE: UTC+0! var today = new Date(), thisMonth = today.getUTCMonth(), // Note: Jan is 0 thisYear = today.getUTCFullYear(), months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; // (A3) MONTH SELECT var select = document.createElement("select"), option = null; select.classList.add("picker-m"); for (var mth in months) { option = document.createElement("option"); option.value = parseInt(mth) + 1; option.text = months[mth]; select.appendChild(option); } select.selectedIndex = thisMonth; select.addEventListener("change", function(){ picker.draw(this); }); dp.appendChild(select); // (A4) YEAR SELECT var yRange = 10; // Year range to show, I.E. from thisYear-yRange to thisYear+yRange select = document.createElement("select"); select.classList.add("picker-y"); for (var y = thisYear-yRange; y < thisYear+yRange; y++) { option = document.createElement("option"); option.value = y; option.text = y; select.appendChild(option); } select.selectedIndex = yRange; select.addEventListener("change", function(){ picker.draw(this); }); dp.appendChild(select); // (A5) DAY SELECT var days = document.createElement("div"); days.classList.add("picker-d"); dp.appendChild(days); // (A6) ATTACH DATE PICKER TO TARGET CONTAINER + DRAW THE DATES picker.draw(select); // (A6-I) INLINE DATE PICKER if (opt.container) { document.getElementById(opt.container).appendChild(dp); } // (A6-P) POPUP DATE PICKER else { // (A6-P-1) MARK THIS AS A "POPUP" var uniqueID = 0; while (document.getElementById("picker-" + uniqueID) != null) { uniqueID = Math.floor(Math.random() * (100 - 2)) + 1; } dp.dataset.popup = "1"; dp.dataset.dpid = uniqueID; // (A6-P-2) CREATE WRAPPER var wrapper = document.createElement("div"); wrapper.id = "picker-" + uniqueID; wrapper.classList.add("picker-wrap"); wrapper.appendChild(dp); // (A6-P-3) ATTACH ONCLICK TO SHOW/HIDE DATEPICKER var target = document.getElementById(opt.target); target.dataset.dp = uniqueID; target.readOnly = true; // Prevent onscreen keyboar on mobile devices target.onfocus = function () { document.getElementById("picker-" + this.dataset.dp).classList.add("show"); }; wrapper.addEventListener("click", function (evt) { if (evt.target.classList.contains("picker-wrap")) { this.classList.remove("show"); } }); // (A6-P-4) ATTACH POPUP DATEPICKER TO BODY document.body.appendChild(wrapper); } }, // (B) DRAW THE DAYS IN MONTH // el : HTML reference to either year or month selector draw : function (el) { // (B1) GET DATE PICKER COMPONENTS var parent = el.parentElement, year = parent.getElementsByClassName("picker-y")[0].value, month = parent.getElementsByClassName("picker-m")[0].value, days = parent.getElementsByClassName("picker-d")[0]; // (B2) DATE RANGE CALCULATION - NOTE: UTC+0! var daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate(), startDay = new Date(Date.UTC(year, month-1, 1)).getUTCDay(), // Note: Sun = 0 endDay = new Date(Date.UTC(year, month-1, daysInMonth)).getUTCDay(), startDay = startDay==0 ? 7 : startDay, endDay = endDay==0 ? 7 : endDay; // (B3) GENERATE DATE SQUARES (IN ARRAY FIRST) var squares = [], disableday = null; if (parent.dataset.disableday) { disableday = JSON.parse(parent.dataset.disableday); } // (B4) EMPTY SQUARES BEFORE FIRST DAY OF MONTH if (parent.dataset.startmon=="1" && startDay!=1) { for (var i=1; i<startDay; i++) { squares.push("B"); } } if (parent.dataset.startmon=="0" && startDay!=7) { for (var i=0; i<startDay; i++) { squares.push("B"); } } // (B5) DAYS OF MONTH // (B5-1) ALL DAYS ENABLED, JUST ADD if (disableday==null) { for (var i=1; i<=daysInMonth; i++) { squares.push([i, false]); } } // (B5-2) SOME DAYS DISABLED else { var thisday = startDay; for (var i=1; i<=daysInMonth; i++) { // CHECK IF DAY IS DISABLED var disabled = disableday.includes(thisday); // DAY OF MONTH, DISABLED squares.push([i, disabled]); // NEXT DAY thisday++; if (thisday==8) { thisday = 1; } } } // (B6) EMPTY SQUARES AFTER LAST DAY OF MONTH if (parent.dataset.startmon=="1" && endDay!=7) { for (var i=endDay; i<7; i++) { squares.push("B"); } } if (parent.dataset.startmon=="0" && endDay!=6) { for (var i=endDay; i<(endDay==7?13:6); i++) { squares.push("B"); } } // (B7) DRAW HTML var daynames = ["Mon", "Tue", "Wed", "Thur", "Fri", "Sat"]; if (parent.dataset.startmon=="1") { daynames.push("Sun"); } else { daynames.unshift("Sun"); } // (B7-1) HTML DATE HEADER var table = document.createElement("table"), row = table.insertRow() cell = null; row.classList.add("picker-d-h"); for (let d of daynames) { cell = row.insertCell(); cell.innerHTML = d; } // (B7-2) HTML DATE CELLS var total = squares.length, row = table.insertRow(), today = new Date(), todayDate = null; if (today.getUTCMonth()+1 == month && today.getUTCFullYear() == year) { todayDate = today.getUTCDate(); } for (var i=0; i<total; i++) { if (i!=total && i%7==0) { row = table.insertRow(); } cell = row.insertCell(); if (squares[i] == "B") { cell.classList.add("picker-d-b"); } else { cell.innerHTML = squares[i][0]; // NOT ALLOWED TO CHOOSE THIS DAY if (squares[i][1]) { cell.classList.add("picker-d-dd"); } // ALLOWED TO CHOOSE THIS DAY else { if (i == todayDate) { cell.classList.add("picker-d-td"); } cell.classList.add("picker-d-d"); cell.addEventListener("click", function(){ picker.pick(this); }); } } } // (B7-3) ATTACH NEW CALENDAR TO DATEPICKER days.innerHTML = ""; days.appendChild(table); }, // (C) CHOOSE A DATE // el : HTML reference to selected date cell pick : function (el) { // (C1) GET ALL COMPONENTS var parent = el.parentElement; while (!parent.classList.contains("picker")) { parent = parent.parentElement; } // (C2) GET FULL SELECTED YEAR MONTH DAY var year = parent.getElementsByClassName("picker-y")[0].value, month = parent.getElementsByClassName("picker-m")[0].value, day = el.innerHTML; // YYYY-MM-DD FORMAT - CHANGE FORMAT HERE IF YOU WANT ! if (parseInt(month)<10) { month = "0" + month; } if (parseInt(day)<10) { day = "0" + day; } var fullDate = year + "-" + month + "-" + day; // (C3) UPDATE SELECTED DATE document.getElementById(parent.dataset.target).value = fullDate; // (C4) POPUP ONLY - CLOSE THE POPUP if (parent.dataset.popup == "1") { document.getElementById("picker-" + parent.dataset.dpid).classList.remove("show"); } } };
Tùy chọn file index.html:
disableday : [2, 7]: Không cho người dùng chọn các ngày thứ 3 và chủ nhật, ví dụ [1, 5] sẽ là thứ 2 và thứ 6
startmon: true: Tuần được bắt đầu từ thứ 2, nếu đổi giá trị thành false thì tuần bắt đầu từ chủ nhật
Và còn rất nhiều tính năng khác bạn có thể tự tìm hiểu!
[khungdownload manguon=”” redirectbanggi=”sharescript-redirect” tenfilezip=”js-datepick.zip” linkbutton1=”https://drive.google.com/file/d/1txeDtUpuNer3CN5uJgl_FFEwjQdAe7q-/view?usp=sharing” tenbutton1=”Link drive” linkbutton2=”https://mega.nz/file/k0Z2gBRC#9TJvD2MDERMF-m872zw_ePMNVx2JE0P1g1R5CrbQ778″ tenbutton2=”Link mega”]