How do I invoke custom constraint validation as part of the native validation event flow?

forms, html, javascript, validation

Solution

As far as I am aware, the checkValidity method calls the browser's internal validation, rather than being what the browser's internal validation calls. Thus if you override it the browser won't notice or care.

In my experience, all that is necessary to implement custom validity is to bind to the change event and then call setCustomValidity with the right value. For example,

myinput.addEventListener('change', function(e) {
    if ( ! myCustomValidityCheck() )
        this.setCustomValidity( 'Uh oh, that didn\'t work!' );
    else
        this.setCustomValidity( '' );
})

Whimsical JSFiddle example

Problem

- 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 invoking `checkValidity` on an empty input? - Why don't I get any feedback from the `console.log` in the event handler bound to `input` && `change`? - How come executing code as a callback to the `change` event doesn't trigger `setCustomValidity`? - What event — if not `input` — is actually triggering my `setCustomValidity`? - Could I produce the same desired end result without overriding `checkValidity`?

Original source

Related problems