Skip to content
Advertisement

Check selected file matches accept attribute on an tag

I wish to prevent the user from uploading a file the server will reject from a page with minimal JavaScript on it, ideally without adding any heavy dependencies like jQuery purely to solve this one problem.

Since I’m not targeting legacy users, I trued using the browser’s form validation system to check if the user has selected a valid file, however it only seems to care if the user selects a file regardless of type.

> i = document.querySelector('input[type=file]')
<input type=​"file" accept=​"image/​*" name=​"attachment" required>​
> i.accept
"image/*"
> i.files[0].type
"application/x-zip-compressed"
> i.checkValidity()
true

Is there a simple way of doing this? The only thing that I have found that comes close is jQuery Validate, but it’s a bit of a heavyweight solution.

Advertisement

Answer

You could just perform a RegExp test — the following converts the wildcard in MIME type strings to match RegExp syntax, and tests that against the input file’s type:

( new RegExp( i.accept.replace( '*', '.*' ) ) ).test( i.files[ 0 ].type )

Demo here.

EDIT:

I eventually found a way to make this functionality seamless with native browser validation behaviour (ie prevent submission for invalid inputs, notify user using native validation warnings), but I’m not exactly sure how the code works or whether it’s good practice (I’ve asked about the stranger parts here). However, this appears to behave as expected, at least in Chrome 31:

void function enhanceFileInputTypeValidityCheck(){
    var inputPrototype      = document.createElement( 'input' ).constructor.prototype;
    var nativeCheckValidity = inputPrototype.checkValidity;

    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 );
        } );
    }
    
    function validateInputs(){
        Array.prototype.forEach.call( document.querySelectorAll( 'input, select' ), function callValidation( input ){
            input.checkValidity();
        } );
    }

    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 );
    }
    
    Array.prototype.forEach.call( [ 'change', 'input' ], function bindValidation( event ){
        document.documentElement.addEventListener( event, validateInputs );
    } );
}();

Demo here (attempt to submit with an invalid file type).

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement