I wrote a similar solution some time ago.
Of course it's just a PoC and can be improved further.
This solution covers the following features:
const pattern = "__/__/____";_x000D_
const patternFreeChar = "_";_x000D_
const validDate = [_x000D_
/^[0-3]$/,_x000D_
/^(0[1-9]|[12]\d|3[01])$/,_x000D_
/^(0[1-9]|[12]\d|3[01])[01]$/,_x000D_
/^((0[1-9]|[12]\d|3[01])(0[13578]|1[02])|(0[1-9]|[12]\d|30)(0[469]|11)|(0[1-9]|[12]\d)02)$/,_x000D_
/^((0[1-9]|[12]\d|3[01])(0[13578]|1[02])|(0[1-9]|[12]\d|30)(0[469]|11)|(0[1-9]|[12]\d)02)[12]$/,_x000D_
/^((0[1-9]|[12]\d|3[01])(0[13578]|1[02])|(0[1-9]|[12]\d|30)(0[469]|11)|(0[1-9]|[12]\d)02)(19|20)/_x000D_
]_x000D_
_x000D_
/**_x000D_
* Validate a date as your type._x000D_
* @param {string} date The date in format DDMMYYYY as a string representation._x000D_
* @throws {Error} When the date is invalid._x000D_
*/_x000D_
function validateStartTypingDate(date) {_x000D_
if ( !date ) return "";_x000D_
_x000D_
date = date.substr(0, 8);_x000D_
_x000D_
if ( !/^\d+$/.test(date) )_x000D_
throw new Error("Please type numbers only");_x000D_
_x000D_
if ( !validDate[Math.min(date.length-1,validDate.length-1)].test(date) ) {_x000D_
let errMsg = "";_x000D_
switch ( date.length ) {_x000D_
case 1:_x000D_
throw new Error("Day in month can start only with 0, 1, 2 or 3");_x000D_
_x000D_
case 2:_x000D_
throw new Error("Day in month must be in a range between 01 and 31");_x000D_
_x000D_
case 3:_x000D_
throw new Error("Month can start only with 0 or 1");_x000D_
_x000D_
case 4: {_x000D_
const day = parseInt(date.substr(0,2));_x000D_
const month = parseInt(date.substr(2,2));_x000D_
const monthName = new Date(0,month-1).toLocaleString('en-us',{month:'long'});_x000D_
_x000D_
if ( month < 1 || month > 12 )_x000D_
throw new Error("Month number must be in a range between 01 and 12");_x000D_
_x000D_
if ( day > 30 && [4,6,9,11].includes(month) )_x000D_
throw new Error(`${monthName} have maximum 30 days`);_x000D_
_x000D_
if ( day > 29 && month === 2 )_x000D_
throw new Error(`${monthName} have maximum 29 days`);_x000D_
break; _x000D_
}_x000D_
_x000D_
case 5:_x000D_
case 6:_x000D_
throw new Error("We support only years between 1900 and 2099, so the full year can start only with 19 or 20");_x000D_
}_x000D_
}_x000D_
_x000D_
if ( date.length === 8 ) {_x000D_
const day = parseInt(date.substr(0,2));_x000D_
const month = parseInt(date.substr(2,2));_x000D_
const year = parseInt(date.substr(4,4));_x000D_
const monthName = new Date(0,month-1).toLocaleString('en-us',{month:'long'});_x000D_
if ( !isLeap(year) && month === 2 && day === 29 )_x000D_
throw new Error(`The year you are trying to enter (${year}) is not a leap year. Thus, in this year, ${monthName} can have maximum 28 days`);_x000D_
}_x000D_
_x000D_
return date;_x000D_
}_x000D_
_x000D_
/**_x000D_
* Check whether the given year is a leap year._x000D_
*/_x000D_
function isLeap(year) {_x000D_
return new Date(year, 1, 29).getDate() === 29;_x000D_
}_x000D_
_x000D_
/**_x000D_
* Move cursor to the end of the provided input element._x000D_
*/_x000D_
function moveCursorToEnd(el) {_x000D_
if (typeof el.selectionStart == "number") {_x000D_
el.selectionStart = el.selectionEnd = el.value.length;_x000D_
} else if (typeof el.createTextRange != "undefined") {_x000D_
el.focus();_x000D_
var range = el.createTextRange();_x000D_
range.collapse(false);_x000D_
range.select();_x000D_
}_x000D_
}_x000D_
_x000D_
/**_x000D_
* Move cursor to the end of the self input element._x000D_
*/_x000D_
function selfMoveCursorToEnd() {_x000D_
return moveCursorToEnd(this);_x000D_
}_x000D_
_x000D_
const input = document.querySelector("input")_x000D_
_x000D_
input.addEventListener("keydown", function(event){_x000D_
event.preventDefault();_x000D_
document.getElementById("date-error-msg").innerText = "";_x000D_
_x000D_
// On digit pressed_x000D_
let inputMemory = this.dataset.inputMemory || "";_x000D_
_x000D_
if ( event.key.length === 1 ) {_x000D_
try {_x000D_
inputMemory = validateStartTypingDate(inputMemory + event.key);_x000D_
} catch (err) {_x000D_
document.getElementById("date-error-msg").innerText = err.message;_x000D_
}_x000D_
}_x000D_
_x000D_
// On backspace pressed_x000D_
if ( event.code === "Backspace" ) {_x000D_
inputMemory = inputMemory.slice(0, -1);_x000D_
}_x000D_
_x000D_
// Build an output using a pattern_x000D_
if ( this.dataset.inputMemory !== inputMemory ) {_x000D_
let output = pattern;_x000D_
for ( let i=0, digit; i<inputMemory.length, digit=inputMemory[i]; i++ ) {_x000D_
output = output.replace(patternFreeChar, digit);_x000D_
}_x000D_
this.dataset.inputMemory = inputMemory;_x000D_
this.value = output;_x000D_
}_x000D_
_x000D_
// Clean the value if the memory is empty_x000D_
if ( inputMemory === "" ) {_x000D_
this.value = "";_x000D_
}_x000D_
}, false);_x000D_
_x000D_
input.addEventListener('select', selfMoveCursorToEnd, false);_x000D_
input.addEventListener('mousedown', selfMoveCursorToEnd, false);_x000D_
input.addEventListener('mouseup', selfMoveCursorToEnd, false);_x000D_
input.addEventListener('click', selfMoveCursorToEnd, false);
_x000D_
<input type="text" placeholder="DD/MM/YYYY" />_x000D_
<div id="date-error-msg"></div>
_x000D_
A link to jsfiddle: https://jsfiddle.net/d1xbpw8f/56/
Good luck!