This JavaScript code snippet provides a scrollable datepicker. It allows users to select dates by scrolling (with the mouse wheel) or swiping (on touch devices).
The code works by capturing scroll and touch events on the date, month, and year containers, adjusting the displayed date, month, or year accordingly. It also prevents accidental scrolling on iOS devices.
This code is helpful for creating a user-friendly datepicker that simplifies date selection with scrolling, making it ideal for web applications requiring date inputs.
How to Create Scroll Datepicker in JavaScript
1. First, load the Google Fonts by adding the following CDN links into the head tag of your HTML document. (Optional)
<link rel="preconnect" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;600&display=swap" rel="stylesheet">
2. Create the HTML structure for your datepicker. You can copy the following HTML code as a starting point. The datepicker is contained within a <div>
with the ID “picker_container.”
<div id="container"> <div class="glass"> <div class="picker"> <h1>Scrollpicker</h1> <div id="picker_container"> <div id="date_container"> <div id="datepicker"></div> <button class="buttone" id="dup" aria-label="date up"><svg width="100%" height="100%" viewBox="0 0 135 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:square;stroke-miterlimit:1.5;"> <path d="M15,44.133l52.067,-29.133l52.066,29.133" style="fill:none;stroke:#000;stroke-width:20px;" /> </svg></button> <button class="buttone bdown" id="ddown" aria-label="date down"><svg width="100%" height="100%" viewBox="0 0 135 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:square;stroke-miterlimit:1.5;"> <path d="M15,15l52.067,29.133l52.066,-29.133" style="fill:none;stroke:#000;stroke-width:20px;" /> </svg></button> </div> <div id="month_container"> <div id="monthpicker"></div> <button class="buttone" id="mup" aria-label="month up"><svg width="100%" height="100%" viewBox="0 0 135 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:square;stroke-miterlimit:1.5;"> <path d="M15,44.133l52.067,-29.133l52.066,29.133" style="fill:none;stroke:#000;stroke-width:20px;" /> </svg></button> <button class="buttone bdown" id="mdown" aria-label="month down"><svg width="100%" height="100%" viewBox="0 0 135 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:square;stroke-miterlimit:1.5;"> <path d="M15,15l52.067,29.133l52.066,-29.133" style="fill:none;stroke:#000;stroke-width:20px;" /> </svg></button> </div> <div id="year_container"> <div id="yearpicker"></div> <button class="buttone" id="yup" aria-label="year up"><svg width="100%" height="100%" viewBox="0 0 135 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:square;stroke-miterlimit:1.5;"> <path d="M15,44.133l52.067,-29.133l52.066,29.133" style="fill:none;stroke:#000;stroke-width:20px;" /> </svg></button> <button class="buttone bdown" id="ydown" aria-label="year down"><svg width="100%" height="100%" viewBox="0 0 135 60" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:square;stroke-miterlimit:1.5;"> <path d="M15,15l52.067,29.133l52.066,-29.133" style="fill:none;stroke:#000;stroke-width:20px;" /> </svg></button> </div> </div> </div> </div> </div>
3. The following CSS code contains the styling for the datepicker interface. You can customize the styling to match your website’s design. The CSS code uses CSS variables (custom properties) to define colors and fonts, making it easy to adjust the look and feel of the datepicker.
:root { --c2: rgba(241, 91, 181, 0.6); /*magenta*/ --c3: rgba(254, 228, 64, 1); /*yellow*/ --c4: rgba(0, 187, 249, 0.5); /*blue*/ --c5: rgba(0, 245, 212, 0.5); /*green*/ --w: rgba(255, 255, 255, 0.1); --fontfam: "Poppins", sans-serif; --color: #000; --bordercolor: rgba(65, 65, 65, 0.1); } .cd__main{ position: relative; min-height: 720px; } #container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(to bottom, var(--c2), var(--w) 75%), radial-gradient(circle at 10% 20%, var(--c3) 40%, var(--w) 40%), linear-gradient(20deg, var(--c5) 30%, var(--w) 30%), linear-gradient(-20deg, var(--c4) 30%, var(--w) 30%); display: flex; align-items: center; justify-content: center; flex-direction: row; } .glass { position: absolute; box-sizing: border-box; width: 90%; height: 65%; background: linear-gradient( 107.18deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.3) 100% ); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-radius: 30px; border: 1px solid rgba(255, 255, 255, 0.7); -webkit-background-clip: padding-box; background-clip: padding-box; } .picker { display: grid; height: 100%; grid-template-columns: 100%; grid-template-rows: 17% 15% 65%; } h1 { padding: 0; margin: 0; font-size: 26px; font-family: var(--fontfam); font-weight: bold; color: var(--color); text-align: center; display: flex; align-items: center; justify-content: center; /*background: #39a;*/ } #picker_container { grid-row-start: 2; width: 100%; display: flex; align-items: center; justify-content: center; flex-direction: row; font-family: var(--fontfam); /*background: #69a;*/ } #pbox { grid-row-start: 3; overflow-y: auto; display: flex; align-items: center; justify-content: flex-start; flex-direction: column; /*background: #34a;*/ } p { width: 80%; font-family: var(--fontfam); text-align: center; } /*Desktop Stuff*/ @media only screen and (min-width: 900px) { .glass { width: 70%; height: 60%; } .picker { grid-template-rows: 30% 20% 40%; } p { width: 50%; } } /*Datepicker Stuff*/ #picker_container { width: 100%; /*background: #56f;*/ display: flex; align-items: center; justify-content: center; flex-direction: row; } #month_container, #date_container, #year_container { grid-row-start: 2; height: 50px; display: grid; grid-template-columns: 110px 30px; grid-template-rows: 50% 50%; margin: 2px; background: linear-gradient( 107.18deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.05) 100% ); backdrop-filter: blur(13px); border-radius: 10px; border: 1px solid rgba(255, 255, 255, 0.3); } #date_container { grid-template-columns: 50px 30px; } #year_container { grid-template-columns: 60px 30px; } #monthpicker, #datepicker, #yearpicker { width: 110px; height: 50px; /*background: var(--pickercolor);*/ border-radius: 10px 0 0 10px; text-align: center; line-height: 50px; color: rgba(0, 0, 0, 1); cursor: ns-resize; } #datepicker { width: 50px; } #yearpicker { width: 60px; } .buttone { background: none; grid-column-start: 2; grid-column-stop: 2; grid-row-start: 1; /*background: var(--pickercolor);*/ background: rgba(255, 255, 255, 0.1); border-radius: 0 10px 0 0; cursor: pointer; border-left: 1px solid var(--bordercolor); touch-action: manipulation; } .bdown { grid-row-start: 2; grid-row-stop: 2; border-radius: 0 0 10px 0; border-top: 1px solid var(--bordercolor); } /*style reset*/ button { display: inline-block; border: none; padding: none; margin: 0; outline: none; -webkit-appearance: none; -moz-appearance: none; } button:hover, button:focus { background: rgba(255, 255, 255, 0.2); } button:focus { outline: none; } button:active { transform: scale(1); } a, a:link, a:visited { color: var(--color); text-decoration-style: dotted; }
4. Finally, include the following JavaScript code within a <script>
tag in your HTML file, or link to an external JavaScript file. It handles the datepicker’s functionality. It captures scroll and touch events on the date, month, and year containers and adjusts the displayed date, month, or year accordingly.
//elements const container = document.getElementById("container"); //setColor(false); const datepicker = document.getElementById("datepicker"); const monthpicker = document.getElementById("monthpicker"); const yearpicker = document.getElementById("yearpicker"); const dup = document.getElementById("dup"); const ddown = document.getElementById("ddown"); const mup = document.getElementById("mup"); const mdown = document.getElementById("mdown"); const yup = document.getElementById("yup"); const ydown = document.getElementById("ydown"); //vars let lastscrollp = 0; let lasttouchp = 0; const months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; //set start date let today = new Date(); let date_now = today.getDate(); let month_now = today.getMonth(); let year_now = today.getFullYear(); console.log(date_now, month_now + 1, year_now); datepicker.innerHTML = date_now + "."; monthpicker.innerHTML = months[month_now]; yearpicker.innerHTML = year_now; //button events dup.addEventListener("click", (e) => { adjustDate(1); }); ddown.addEventListener("click", (e) => { adjustDate(-1); }); mup.addEventListener("click", (e) => { adjustMonth(1); }); mdown.addEventListener("click", (e) => { adjustMonth(-1); }); yup.addEventListener("click", (e) => { adjustYear(1); }); ydown.addEventListener("click", (e) => { adjustYear(-1); }); //wheel & touch events datepicker.addEventListener("wheel", (e) => { console.log("scroll " + e.deltaY); //debug.innerHTML = e.deltaY; lastscrollp = lastscrollp + e.deltaY; if (lastscrollp > 0 && e.deltaY < 0) { lastscrollp = 0; } if (lastscrollp < 0 && e.deltaY > 0) { lastscrollp = 0; } console.log(lastscrollp); if (lastscrollp > 10 || lastscrollp < -10) { adjustDate(e.deltaY); lastscrollp = 0; } }); datepicker.addEventListener("touchstart", (e) => { lasttouchp = e.changedTouches[0].pageY; }); datepicker.addEventListener("touchmove", (e) => { e.preventDefault(); console.log("touch " + e.changedTouches[0].pageY); let diff = e.changedTouches[0].pageY - lasttouchp; let updown = 0; if (diff > 10) { updown = -1; lasttouchp = e.changedTouches[0].pageY; } else { if (diff < -10) { updown = 1; lasttouchp = e.changedTouches[0].pageY; } } adjustDate(updown); }); monthpicker.addEventListener("wheel", (e) => { console.log("scroll " + e.deltaY); //debug.innerHTML = e.deltaY; lastscrollp = lastscrollp + e.deltaY; if (lastscrollp > 0 && e.deltaY < 0) { lastscrollp = 0; } if (lastscrollp < 0 && e.deltaY > 0) { lastscrollp = 0; } console.log(lastscrollp); if (lastscrollp > 10 || lastscrollp < -10) { adjustMonth(e.deltaY); lastscrollp = 0; } }); monthpicker.addEventListener("mouseout", (e) => { setMaxDays(); }); monthpicker.addEventListener("touchstart", (e) => { lasttouchp = e.changedTouches[0].pageY; }); monthpicker.addEventListener("touchmove", (e) => { e.preventDefault(); console.log("touch " + e.changedTouches[0].pageY); let diff = e.changedTouches[0].pageY - lasttouchp; let updown = 0; if (diff > 10) { updown = -1; lasttouchp = e.changedTouches[0].pageY; } else { if (diff < -10) { updown = 1; lasttouchp = e.changedTouches[0].pageY; } } adjustMonth(updown); }); monthpicker.addEventListener("touchend", (e) => { setMaxDays(); }); yearpicker.addEventListener("wheel", (e) => { console.log("scroll " + e.deltaY); //debug.innerHTML = e.deltaY; lastscrollp = lastscrollp + e.deltaY; if (lastscrollp > 0 && e.deltaY < 0) { lastscrollp = 0; } if (lastscrollp < 0 && e.deltaY > 0) { lastscrollp = 0; } console.log(lastscrollp); if (lastscrollp > 10 || lastscrollp < -10) { adjustYear(e.deltaY); lastscrollp = 0; } }); yearpicker.addEventListener("mouseout", (e) => { setMaxDays(); }); yearpicker.addEventListener("touchstart", (e) => { lasttouchp = e.changedTouches[0].pageY; }); yearpicker.addEventListener("touchmove", (e) => { e.preventDefault(); console.log("touch " + e.changedTouches[0].pageY); let diff = e.changedTouches[0].pageY - lasttouchp; let updown = 0; if (diff > 10) { updown = -1; lasttouchp = e.changedTouches[0].pageY; } else { if (diff < -10) { updown = 1; lasttouchp = e.changedTouches[0].pageY; } } adjustYear(updown); }); yearpicker.addEventListener("touchend", (e) => { setMaxDays(); }); //functions function adjustDate(v) { if (v != 0) { let maxdays = new Date(year_now, month_now + 1, 0).getDate(); if (v > 0) { if (date_now === maxdays) { date_now = 1; } else { date_now = date_now + 1; } } else { if (date_now === 1) { date_now = maxdays; } else { date_now = date_now - 1; } } datepicker.innerHTML = date_now + "."; window.navigator.vibrate(8); } } function adjustMonth(v) { if (v != 0) { if (v > 0) { if (month_now === 11) { month_now = 0; } else { month_now = month_now + 1; } } else { if (month_now === 0) { month_now = 11; } else { month_now = month_now - 1; } } monthpicker.innerHTML = months[month_now]; window.navigator.vibrate(8); } } function adjustYear(v) { if (v != 0) { if (v > 0) { year_now = year_now + 1; } else { year_now = year_now - 1; } if (year_now < 0) { yearpicker.innerHTML = year_now * -1 + " BC"; } else { yearpicker.innerHTML = year_now; } window.navigator.vibrate(8); if(year_now === 2020){ setColor(true); }else{ setColor(false); } } } function setMaxDays(){ let maxdays = new Date(year_now, month_now + 1, 0).getDate(); if (date_now > maxdays) { date_now = maxdays; datepicker.innerHTML = date_now + "."; } } function setColor(g){ if (g) { container.style.setProperty("--c2", "rgba(166, 166, 166, .6)"); container.style.setProperty("--c3", "rgba(158, 158, 158, 1)"); container.style.setProperty("--c4", "rgba(125, 125, 125, .5)"); container.style.setProperty("--c5", "rgba(122, 122, 122, .5)"); } else { container.style.setProperty("--c2", "rgba(241, 91, 181, .6)"); container.style.setProperty("--c3", "rgba(254, 228, 64, 1)"); container.style.setProperty("--c4", "rgba(0, 187, 249, .5)"); container.style.setProperty("--c5", "rgba(0, 245, 212, .5)"); } } //prevent ape-scrolling on iOS let letTouchMove = false; container.addEventListener("touchmove", (e) => { if (letTouchMove === false) { e.preventDefault(); } }); const pbox = document.getElementById("pbox"); pbox.addEventListener("touchstart", (e) => { letTouchMove = true; }); pbox.addEventListener("touchend", (e) => { letTouchMove = false; });
That’s it! Hopefully, you’ve created a scrollable datepicker using JavaScript. This user-friendly datepicker simplifies date selection for your web application users. If you have any questions or suggestions, feel free to comment below.
Similar Code Snippets:
I code and create web elements for amazing people around the world. I like work with new people. New people new Experiences.
I truly enjoy what I’m doing, which makes me more passionate about web development and coding. I am always ready to do challenging tasks whether it is about creating a custom CMS from scratch or customizing an existing system.