- 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.logwhen invokingcheckValidityon an empty input? - Why don't I get any feedback from the
console.login the event handler bound toinput&&change? - How come executing code as a callback to the
changeevent doesn't triggersetCustomValidity? - What event — if not
input— is actually triggering mysetCustomValidity? - Could I produce the same desired end result without overriding
checkValidity?