I am building a calendar picker to accompany (and replace the default calendar date picker provided by some browsers) an input
of type="date"
.
It is documented that for "simplicity" that Chrome's (and possibly other browsers) date input allows invalid dates to be inputted, but they will fail to validate (ie. 31/06/2017 can be inputted, but produces a "value" of "").
When an invalid date is entered into the input, how can I get the value of that invalid date?
var date_input = document.getElementById('date_input');
var output_div = document.getElementById('output_div');
output_div.innerHTML = date_input.value;
date_input.addEventListener('input', function(e){
output_div.innerHTML = e.target.value;
});
<p>To quickly input an invalid date, select the day field and press the down arrow (on Chrome OSX this displays an empty div below the input instead of the invalid date). This will select 31/06/2017</p>
<input type="date" value="2017-06-01" id="date_input" />
<br />
<br />
<div id="output_div" />
var date_input = document.getElementById( 'date_input' );
var output_div = document.getElementById( 'output_div' );
output_div.textContent = date_input.valueAsDate; // :-)
date_input.addEventListener( 'input', function( evt ){
var val = date_input.value,
sel = document.getSelection();
if ( date_input.validity.badInput ) {
// date_input.focus(); // with or without this,
// date_input.select(); // this doesn't work but,
// due to the added <span> wrapper:
sel.selectAllChildren( date_input.parentElement );
// this does. So:
val = sel.toString() || function() {
var copied = document.execCommand( 'copy' );
sel.empty(); // clear the selection
return "Invalid date" + ( copied ? " copied to clipboard." : "" );
}();
}
output_div.textContent = val;
});
#output_div {
margin-top: 1em;
}
<p>To quickly input an invalid date, select the day field and press the down arrow (on Chrome OSX this displays an empty div below the input instead of the invalid date). This will select 31/06/2017</p>
<span><input type="date" value="2017-06-01" id="date_input"></span>
<div id="output_div"></div>
Apparently since the value
shown is actually the output of the shadow DOM child of the <input>
, and we can't access the user-agent
's shadow DOM, the visible value is effectively unknown to JS.
For accessibility reasons, the visible value has to be accessible to screen readers and the like, and can also be manually copied to the clipboard.
Having tried multiple ways of programmatically selecting and copying the text, I have run out of ideas and patience, and could only manage to copy it to the clipboard.
aria-value*
attributes of the #shadow-root (user-agent)
with JS, then you have a way forward.selection
string to a var
, you have another way forward.Since the invalid input is limited to being predictably out of range, you could monitor what the last valid value
was before it was changed to something invalid, then calculate what it must surely now be.
30/06/2017
before a change that rendered it invalid, and 29/06/2017
, 30/05/2017
, 30/07/2017
, 30/06/2016
and 30/06/2018
are all valid, the invalid date must be 31/06/2017
.I have not experimented (yet) with making that work, but am confident it can. I would be posting that solution with this answer, but half my day has been swallowed by this question already and I'm hungry. If a sincere interest is expressed, I will happily try and provide the codez.
This will display invalid dates if they're created using the arrow buttons, and only if the last date was valid. I could probably find a way around that, but officially give up! Life's too short.
According to MDN:
... the displayed date format will be chosen based on the set locale of the user's operating system.
This would make tracking keystrokes unreasonably non-trivial.
However, I just discovered <input type="datetime-local">
which might solve that issue, and could possibly solve others.
But trying to force this to work is beginning to feel like trying to insert a watermelon where one would not usually be expected to fit.
It's a ham-fisted kludge with little to no charm, that is likely to break if anyone looks at it too hard. Best disclaimer ever right ;)
var date = "",
input = document.querySelector( "input" ),
output = document.querySelector( "output" ),
bits = function( d ) {
return [ d.getFullYear(), d.getMonth() + 1, d.getDate() ];
},
adjustedDate = function( o ) {
var d = new Date( date ),
b = bits( d ),
year = b[ 0 ],
month = b[ 1 ],
day = b[ 2 ];
switch ( o.i ) {
case "d": return [ year, month, ( day + o.v ) || 31 ];
case "m": return [ year, month + o.v, day ];
case "y": return [ year + o.v, month, day ];
}
},
calcDate = function() {
var ad, vd, rd;
[ { i: "d", v: 1 },
{ i: "d", v: -1 },
{ i: "m", v: 1 },
{ i: "m", v: -1 },
{ i: "y", v: 1 },
{ i: "y", v: -1 }
].forEach( ( v ) => {
ad = adjustedDate( v ).join( "-" );
vd = bits( new Date( ad ) ).join( "-" );
if ( ad !== vd ) {
rd = ad;
}
} );
return rd.split( "-" ).map( ( v ) => {
v = v.toString();
return v.length === 1 ? "0" + v : v;
} ).join( "-" ); // tired so mental
},
showDate = function() {
output.textContent = date = ( input.value || calcDate() );
};
showDate();
input.addEventListener( "input", showDate, false );
output {
margin-left: 1em;
}
<p>To quickly input an invalid date, select the day field and press the down arrow. This will select 31/06/2017 and output the desired invalid date string.</p>
<form><input type="date" value="2017-06-01"><output></output></form>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With