- The following is based on testing with Chrome 31
- TL;DR: How come the code in this fiddle works on attempted submit, but doesn't in this one on manual invocation? Is there a better way of doing it?
In answering a question asking for a way of validating an <input type="file">
against its accept
attribute (the native behaviour will work with the resulting file dialog to present a filter by default, but it isn't enforced), I wanted to see what options were available to integrate this custom validation into HTML5's scripted Constraint validation.
My initial skim-reading of the API led me to overwrite the native checkValidity
method, in the belief that this would be called by default whenever the browser saw fit to validate the code (jsFiddle):
void function enhanceFileInputTypeValidityCheck(){
var inputPrototype = document.createElement( 'input' ).constructor.prototype;
var nativeCheckValidity = inputPrototype.checkValidity;
// This is my custom validation function
function validateFileInputType( input ){
var MIMEtype = new RegExp( input.accept.replace( '*', '.\*' ) );
return Array.prototype.every.call( input.files, function passesAcceptedFormat( file ){
return MIMEtype.test( file.type );
} );
}
// My attempt at getting the behaviour to happen
inputPrototype.checkValidity = function enhancedCheckValidity(){
if( this.type === 'file' && this.accept && this.files && this.files.length ){
if( !validateFileInputType( this ) ){
this.setCustomValidity( 'Please only submit files of type ' + this.accept );
return false;
}
}
return nativeCheckValidity.apply( this );
}
}();
This effectively enhances checkValidity
to return false
when my conditions aren't met, and falls through to native behaviour if they are, but setCustomValidity
isn't working as expected: it doesn't throw any exceptions, but doesn't display any error message either. Crucially, my return false
in checkValidity
doesn't prevent the form from submitting.
After some fiddling I ended up with this event handler, which along with the code posted above finally achieves the desired aims — namely, preventing submission according to the custom criteria and feeding back to the user (jsFiddle):
$( 'input' ).on( 'change input', function( event ){
this.checkValidity();
} );
I'm mystified as to how this is happening: according to logging the event type, change
will execute checkValidity
, but won't display the message; meanwhile attepting to submit the form does implement my setCustomValidity
, but won't log the event type: however the implication is that this is an input
event, since that's the only other event the handler is listening for — but (and this is where it gets really weird), unbinding from change
(which wasn't working anyway) leaving only input
as a trigger for my imperative callback doesn't prevent submission at all anymore.
I'm still at a loss for the 'correct' way of binding my custom validation into the native validation flow, since binding to change
and event
to trigger expected behaviour when neither of those events is actually taking place just seems far too hacky to be intended usage.
Bonus points for explaining any of the following:
- Why don't I get any feedback from my custom validation
console.log
when invokingcheckValidity
on an empty input? - Why don't I get any feedback from the
console.log
in the event handler bound toinput
&&change
? - How come executing code as a callback to the
change
event doesn't triggersetCustomValidity
? - What event — if not
input
— is actually triggering mysetCustomValidity
? - Could I produce the same desired end result without overriding
checkValidity
?