Simple Date Picker in Pure Javascript CSS – Free Code Download

There sure are a lot of date pickers on the Internet for jQuery, Bootstrap, React, and whatever else. But I understand, it really does not make any sense to load an entire library, just for the sake of a simple plugin. So here it is, a sharing of my simple lightweight date picker, all done in pure CSS Javascript – Read on!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

TABLE OF CONTENTS

Download & Demo How It Works Useful Bits
The End

 

 

DOWNLOAD & DEMO

Here is the download link to the script, and a short section for you guys who just want to use the script quickly in your own project.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

SOURCE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

EXAMPLE 1) POPUP DATE PICKER

1-popup.html
<!-- (A) LOAD DATE PICKER -->
<link href="dp-light.css" rel="stylesheet">
<script defer src="datepicker.js"></script>
 
<!-- (B) THE HTML -->
<input type="text" id="input-pop" placeholder="Popup"/>
 
<!-- (C) ATTACH DATE PICKER ON LOAD -->
<script>
window.addEventListener("load", () => {
  picker.attach({ target: "input-pop" });
});
</script>

This is the simplest way to use the date picker.

  1. Load the date picker CSS and Javascript. Doh.
  2. Define the HTML <input> field, give it an id.
  3. Use picker.attach() to attach the date picker – Just specify the target HTML field (id of the HTML field).

 

 

EXAMPLE 2) INLINE DATE PICKER

2-inline.html
<!-- (B) THE HTML -->
<input type="text" id="input-inline" placeholder="Inline"/>
<div id="pick-inline"></div>
 
<!-- (C) ATTACH DATE PICKER ON LOAD -->
<script>
window.addEventListener("load", () => {
  picker.attach({
    target: "input-inline",
    container: "pick-inline"
  });
});
</script>

Pretty much the same, but we now also define a <div> to spawn the date picker inline.

 

EXAMPLE 3) ALL DATE PICKER OPTIONS

3-options.html
<!-- (B) THE HTML -->
input type="text" id="input-opt" placeholder="Options"/>
<div id="pick-opt"></div>
 
<!-- (C) ATTACH DATE PICKER ON LOAD -->
<script>
window.addEventListener("load", () => {
  picker.attach({
    target: "input-opt",
    container: "pick-opt",
    disableday : [2, 7], 
    startmon: true, 
    yrange: 5, 
    onpick: () => { alert("PICKED"); } 
  });
});
</script>

The date picker also takes in a couple of options:

  • disableday An array to disable certain days. For example, [2, 7] will disable Tuesdays and Sundays.
  • startmon To set if the week starts on a Monday. By default, this is false and the week starts on Sunday.
  • yrange The selectable year range. This defaults to +/- 10 years.
  • onpick Function to run upon picking a date.

 

 

HOW IT WORKS

In this section, we will walk through the mechanics of how the date picker works – This script is open source, so feel free to modify it as you please.

 

PART A) ATTACH DATE PICKER

datepicker.js
// (A) ATTACH DATEPICKER TO TARGET
// target: field to populate
// container: generate datepicker in here (for inline datepicker)
// startmon: start on mon? (optional, default false)
// yrange: year select range (optional, default 10)
// disableday: days to disable, e.g. [2,7] to disable tue and sun (optional)
// onpick : function to call on select date (optional)
instances : [],
attach : (opt) => {
  // (A1) SET DEFAULT OPTIONS & REGISTER INSTANCE
  opt.target = document.getElementById(opt.target);
  opt.target.readOnly = true; // PREVENT ONSCREEN KEYBOARD
  if (opt.container) { opt.container = document.getElementById(opt.container); }
  opt.startmon = opt.startmon ? true : false;
  opt.yrange = opt.yrange ? opt.yrange : 10;
  const id = picker.instances.length;
  picker.instances.push(opt);
  let inst = picker.instances[id];
 
  // (A2) TEMP VARS + CURRENT MONTH YEAR (UTC+0)
  let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
  temp, today = new Date(),
  thisMonth = today.getUTCMonth(), // JAN IS 0
  thisYear = today.getUTCFullYear();
 
  // (A3) GENERATE HTML
  // (A3-1) HTML DATEPICKER WRAPPER
  inst.hPick = document.createElement("div");
  inst.hPick.classList.add("picker");
 
  // (A3-2) HTML MONTH SELECT
  inst.hMonth = document.createElement("select");
  inst.hMonth.classList.add("picker-m");
  for (let m in months) {
    temp = document.createElement("option");
    temp.value = +m + 1;
    temp.text = months[m];
    inst.hMonth.appendChild(temp);
  }
  inst.hMonth.selectedIndex = thisMonth;
  inst.hMonth.onchange = () => { picker.draw(id); };
  inst.hPick.appendChild(inst.hMonth);
 
  // (A3-3) HTML YEAR SELECT
  inst.hYear = document.createElement("select");
  inst.hYear.classList.add("picker-y");
  for (let y = thisYear-inst.yrange; y < thisYear+inst.yrange; y++) {
    temp = document.createElement("option");
    temp.value = y;
    temp.text = y;
    inst.hYear.appendChild(temp);
  }
  inst.hYear.selectedIndex = inst.yrange;
  inst.hYear.onchange = () => { picker.draw(id); };
  inst.hPick.appendChild(inst.hYear);
 
  // (A3-4) HTML DAYS
  inst.hDays = document.createElement("div");
  inst.hDays.classList.add("picker-d");
  inst.hPick.appendChild(inst.hDays);
  picker.draw(id);
 
  // (A4) INLINE DATEPICKER - ATTACH INTO CONTAINER
  if (inst.container) { inst.container.appendChild(inst.hPick); }
 
  // (A5) POPUP DATEPICKER - ATTACH INTO HTML BODY
  else {
    // (A5-1) FULL PAGE WRAPPER
    inst.hWrap = document.createElement("div");
    inst.hWrap.classList.add("picker-wrap");
    inst.hWrap.appendChild(inst.hPick);
 
    // (A5-2) CLICK TO TOGGLE DATEPICKER
    inst.target.onfocus = () => {
      inst.hWrap.classList.add("show");
    };
    inst.hWrap.onclick = (evt) => { if (evt.target == inst.hWrap) {
      inst.hWrap.classList.remove("show");
    }};
 
    // (A5-3) ATTACH POPUP DATEPICKER
    document.body.appendChild(inst.hWrap);
  }
}

You have already seen this one – picker.attach() is used to attach the date picker to your specified container. It’s long-winded but straightforward:

  • Register the date picker and options into picker.instances. Yes, this is to distinguish which is which if multiple date pickers are created on the same page.
  • Generate the HTML of the date picker – We will go through that below.

 

 

PART B) DRAW DAYS IN MONTH

datepicker.js
// (B) DRAW DAYS IN MONTH
draw : (id) => {
  // (B1) CRAZY VARS & CALCULATIONS
  // (B1-1) GET INSTANCE + SELECTED MONTH YEAR
  let inst = picker.instances[id],
      month = inst.hMonth.value,
      year = inst.hYear.value;
 
  // (B1-2) DATE RANGE CALCULATION (UTC+0)
  let daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate(),
      startDay = new Date(Date.UTC(year, month-1, 1)).getUTCDay(), // SUN IS 0
      endDay = new Date(Date.UTC(year, month-1, daysInMonth)).getUTCDay();
      startDay = startDay==0 ? 7 : startDay,
      endDay = endDay==0 ? 7 : endDay;
 
  // (B1-3) TODAY (FOR HIGHLIGHTING "TODAY'S DATE CELL")
  let today = new Date(), todayDate = null;
  if (today.getUTCMonth()+1 == month && today.getUTCFullYear() == year) {
    todayDate = today.getUTCDate();
  }
 
  // (B1-4) DAY NAMES
  let daynames = ["Mon", "Tue", "Wed", "Thur", "Fri", "Sat"];
  if (inst.startmon) { daynames.push("Sun"); }
  else { daynames.unshift("Sun"); }
 
  // (B1-5) FOR GENERATING DATE SQUARES
  let table, row, cell, squares = [];
 
  // (B2) CALCULATE DATE SQUARES ARRAY
  // (B2-1) EMPTY SQUARES BEFORE FIRST DAY OF MONTH
  if (inst.startmon && startDay!=1) {
    for (let i=1; i<startDay; i++) { squares.push("B"); }
  }
  if (!inst.startmon && startDay!=7) {
    for (let i=0; i<startDay; i++) { squares.push("B"); }
  }
 
  // (B2-2) DAYS OF MONTH (SOME DAYS DISABLED)
  if (inst.disableday) {
    let thisDay = startDay;
    for (let i=1; i<=daysInMonth; i++) {
      squares.push([i, inst.disableday.includes(thisDay)]);
      thisDay++;
      if (thisDay==8) { thisDay = 1; }
    }
  }
 
  // (B2-3) DAYS OF MONTH (ALL DAYS ENABLED)
  else {
    for (let i=1; i<=daysInMonth; i++) { squares.push([i, false]); }
  }
 
  // (B2-4) EMPTY SQUARES AFTER LAST DAY OF MONTH
  if (inst.startmon && endDay!=7) {
    for (let i=endDay; i<7; i++) { squares.push("B"); }
  }
  if (!inst.startmon && endDay!=6) {
    for (let i=endDay; i<(endDay==7?13:6); i++) { squares.push("B"); }
  }
 
  // (B3) DRAW HTML
  // (B3-1) HTML DAY NAMES HEADER
  table = document.createElement("table");
  row = table.insertRow();
  row.classList.add("picker-d-h");
  for (let d of daynames) {
    cell = row.insertCell();
    cell.innerHTML = d;
  }
 
  // (B3-2) HTML DATE CELLS
  row = table.insertRow();
  for (let i=0; i<squares.length; i++) {
    if (i!=squares.length && i%7==0) { row = table.insertRow(); }
    cell = row.insertCell();
    if (squares[i] == "B") { cell.classList.add("picker-d-b"); }
    else {
      // CELL DATE
      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 (squares[i][0] == todayDate) { cell.classList.add("picker-d-td"); }
        cell.classList.add("picker-d-d");
        cell.onclick = () => { picker.pick(id, squares[i][0]); }
      }
    }
  }
 
  // (B4) ATTACH DAYS TO DATEPICKER
  inst.hDays.innerHTML = "";
  inst.hDays.appendChild(table);
}

This function deals with the generation of the HTML days in months. It is fired from picker.attach(), and whenever the month/year is changed. Some crazy calculations are involved here:

  • (B1) Get the selected month/year. Calculate the days in the month, the start date, end date, etc…
  • (B2) Take note, we do not generate the HTML immediately. We calculate the days and keep them in the array squares.
    • Basically, every month is not guaranteed to start on a Sunday (or Monday). So there may be a number of “blank squares” before the first day of the month – This is padded by pushing B into squares.
    • Then the individual days will be populated into squares, these are arrays with 2 elements. The first element is the day of the month, the second is a boolean to indicate if this day is disabled.
    • Every month is also not guaranteed to end on a Saturday (or Sunday). So if necessary, we do the same and pad squareswith B after the last day of the month.
  • (B3 & B4) The actual HTML generation.

 

 

PART C) PICK A DATE

datepicker.js
// (C) CHOOSE A DATE
pick : (id, day) => {
  // (C1) GET MONTH YEAR
  let inst = picker.instances[id],
      month = inst.hMonth.value,
      year = inst.hYear.value;
 
  // (C2) FORMAT & SET SELECTED DAY (YYYY-MM-DD)
  if (+month<10) { month = "0" + month; }
  if (+day<10) { day = "0" + day; }
  inst.target.value = `${year}-${month}-${day}`;
 
  // (C3) POPUP ONLY - CLOSE
  if (inst.container === undefined) {
    inst.hWrap.classList.remove("show");
  }
 
  // (C4) CALL ON PICK IF DEFINED
  if (inst.onpick) { inst.onpick(); }
}

As you can guess, this function simply gets the selected date and returns a nicely formatted string. I have set it to use the ISO 8601 date format of YYYY-MM-DD, but feel free to change it as you see fit.

 

EXTRA) DATE PICKER HTML & CSS

This is the HTML that the Javascript generates… A little messy, but feel free to change the CSS theme to your own liking.

 

USEFUL BITS & LINKS

That’s all for this project, and here is a small section on some extras that may be useful to you.

 

DISABLING CERTAIN DAYS

Everyone has a different requirement –

  • Disable specific days.
  • Disable past days.
  • Allow only past days (disable future days).
  • Only enable from tomorrow to N days later.
  • Disable X on the first and third weeks of the month, disable Y on the second and fourth weeks of the month.

Yep, there is no end to these “custom rules”. If I implement all of them, it won’t be a “simple date picker” anymore. So it’s up to you to do your own custom restrictions. Long story short –

  • (A) Add your own custom restriction option to attach().
  • (B) Modify draw() to also include your new restrictions.

 

 

DO SERVER-SIDE CHECKS!

Yes, the date picker is based on UTC+0, but “errors” still can happen. For you guys who are lost, the dates are based on the users’ devices. This can be easily changed, and potentially mess things up. So always do checks when the date/time is submitted, based on the server’s current time.

 

DATE RANGE PICKER

Sadly, this is another one that is “not simple”. Modify draw() and pick() if you wish. But otherwise, the easy way is to just create 2 date pickers – “Starting” and “until”. Upon submission, check that “until” is a later date than “starting”.

 

COMPATIBILITY CHECKS

Works on all modern browsers. Not on the old Internet Exploders.

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you have anything to share with this guide, please feel free to comment below. Good luck and happy coding!

35 thoughts on “Simple Date Picker in Pure Javascript CSS – Free Code Download”

  1. Hi, Thanks for your script. It is exactly what I was looking for.
    However, I think there’s a bug in the “B7-2 HTML DATE CELLS” section. Actually the “today” date is not properly marked as “picker-d-td” in the HTML.
    So the line
    if (i == todayDate) { cell.classList.add("picker-d-td"); }
    should be
    if (squares[i][0] == todayDate) { cell.classList.add("picker-d-td"); }
    And then the “today” date got the right CSS class.
    What do you think?

  2. I love this datepicker! Thank you for sharing it! I wanted to have the selected date highlighted so I created a new class called picker-d-sd and then added the following to the end of your script in the C3 section:

    var elems = document.querySelector(".picker-d-sd");
    if(elems !==null){
      elems.classList.remove("picker-d-sd");
    }
    el.className = "picker-d-sd";

    This allows the selected date (and only the selected date) to be highlighted in whatever way one wants to style the picker-d-sd class.

    1. Thanks for sharing! Just a small problem with this though – This is pretty much “temporary”. Once the user selects another month/year, the selected day will “disappear”.

      1. Good point. For my purposes that’s okay. If my users are choosing a new month or year, they are probably moving on from the selected day and will choose a new one. If anyone can offer a “sticky” solution, I’d love to see it, though!

Leave a Comment

Your email address will not be published. Required fields are marked *