<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <title>File Uploader</title>        
        <style>
            /* Style for demo, not required */
            body { font-size: 62.5%; margin: 50px; }
            fieldset { padding: .6em 0;border:0;border-top: 1px dotted #ddd; }
            legend { font-size: 1.5em; font-weight: bold; color: #555; padding: .5em 1em .5em 0; background: #fff;  }
            label { font-size: 1.4em; display: block; margin: .5em 10px .5em 0;  }
            input#upload { background: #aaa url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.bg-btn.png) bottom repeat-x; padding: .4em 1.2em;border: 1px solid #aaa; color: #222; font-size: 1.2em; font-weight: bold; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; cursor: pointer; margin: 2em 0; }
            input#upload:hover { background: #eee; color: #111; border-color:#777; }

        </style>
    </head>
    <body>    
    
    <!-- realistic form attributes: <form action="#" method="post" enctype="multipart/form-data"> -->
    <form action="#" onsubmit="return false;">
      <p>jQuery Custom File Upload Input (Enhancements)</p>
      <p>This modification is a derived from the original work by Filament Group: https://filamentgroup.com/lab/jquery_custom_file_input_book_designing_with_progressive_enhancement/ </p>
            <p>File Input Examples</p>

            <label for="file_A">Default Style</label>
            <input type="file" name="file" id="file_A"/>

            <label for="file_B">Percentage Width</label>
            <input type="file" name="file" id="file_B"/>

            <label for="file_C">Custom Text</label>
            <input type="file" name="file" id="file_C"/>

            <label for="file_D">Test Auto-truncated file name (Try selecting a file with a long name and see what happens)</label>
            <input type="file" name="file" id="file_D"/>

            <label for="file_E">Button Only (I actually used this once to mashup with another jQuery plugin which enables multiple file uploads using a single button)</label>
            <input type="file" name="file" id="file_E"/>

            <label for="file_F">onChange event handler (This example writes the function arguments to the JavaScript Console, e.g. Firebug)</label>
            <input type="file" name="file" id="file_F"/>

            <input type="submit" name="upload" id="upload" value="Upload photo" />
    </form>


    </body>
</html>











/**
 * ##1: Removed fixed with of .customfile
 * ##2: Added white-space: nowrap for text
 */
html, body {
    background-color:#222222;
    color:white;
}

*, p {
    font-family: Consolas;
}

/*custom upload elements*/
.customfile-input {
    position:     absolute;
    height:       100px;
    cursor:       pointer;
    background:   transparent;
    border:       0;
    opacity:      0;
    -moz-opacity: 0;
    filter:       progid:DXImageTransform.Microsoft.Alpha(opacity = 0);
    z-index:      999;
}

.customfile {
    /*width: 400px;*/     /* ##1 -- */
    background:            #666;
    cursor:                pointer;
    overflow:              hidden;
    padding:               2px;
    border:                1px solid #444;
    -moz-border-radius:    7px;
    -webkit-border-radius: 7px;
    border-radius:         7px;
    position:              relative;

    /* ##2 ++ */
    white-space: nowrap;
}

.customfile-disabled {
    opacity: .5;
    filter:  progid:DXImageTransform.Microsoft.Alpha(opacity = 0);
    cursor:  default;
}

.customfile-feedback {
    /*display:    block;*/
    margin:     1px 1px 1px 5px;
    font-size:  1.2em;
    color:      #fff;
    font-style: italic;
    padding:    .3em .6em;
}

.customfile-feedback-populated {
    color:        #fff;
    font-style:   normal;
    font-weight:  bold;
    padding-left: 20px;
    background:   url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.icon-generic.gif) left 4px no-repeat;
}

.customfile-button {
    border:                1px solid #999;
    background:            #333 url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.bg-submit.gif) bottom repeat-x;
    color:                 #fff;
    font-weight:           bold;
    float:                 right;
    /*width:                 50px;*/ /* ##1 -- */
    padding:               .3em .6em;
    text-align:            center;
    text-decoration:       none;
    font-size:             1.2em;
    -moz-border-radius:    5px;
    -webkit-border-radius: 5px;
    border-radius:         5px;
}

.customfile-hover .customfile-button, .customfile-focus .customfile-button {
    color:        #111;
    background:   #aaa url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.bg-btn.png) bottom repeat-x;
    border-color: #aaa;
    padding:      .3em .6em;
}

.customfile-focus .customfile-button {
    outline: 1px dotted #ccc;
}

/*file type icons*/
.customfile-ext-jpg, .customfile-ext-gif, .customfile-ext-png, .customfile-ext-jpeg, .customfile-ext-bmp {
    background-image: url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.icon-image.gif);
}

.customfile-ext-mp3, .customfile-ext-mp4, .customfile-ext-mov, .customfile-ext-swf, .customfile-ext-wav, .customfile-ext-m4v {
    background-image: url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.icon-media.gif);
}

.customfile-ext-zip, .customfile-ext-tar, .customfile-ext-sit {
    background-image: url(https://dl.dropbox.com/u/20165443/jsFiddle/images/jquery.fileinput.icon-zip.gif);
}













/**
 * --------------------------------------------------------------------
 * jQuery customfileinput plugin
 * Author: Scott Jehl, scott@filamentgroup.com
 * Copyright (c) 2009 Filament Group 
 * licensed under MIT (filamentgroup.com/examples/mit-license.txt)
 * --------------------------------------------------------------------
 */


/**
 * Modifications: Terry Young (terryyounghk at gmail dot com)
 *
 * ##1: Added 'option' parameter. Initially for optionally specifying a width. Default is 'inherit' (i.e. not specified)
 * ##2: Added options 'buttonText', 'inputText' and 'changeText'
 * ##3: Added option 'maxFileSize'. For this technique, @see: http://www.tizag.com/htmlT/htmlupload.php
 * ##4: Added auto-truncating the file name if text exceeds the available width (Uses the window.onresize event)
 * ##5: Added option 'showInputText'. Default is true
 * ##6: Added event handler 'onChange'
 */
$.fn.customFileInput = function(options){

    // ##1 ++
    var defaults = {
        width: 'inherit',
        buttonText: 'Browse',
        changeText: 'Change',
        inputText: 'No file selected',
        showInputText: true,
        maxFileSize: 0, // ##3 ++

        onChange: $.noop
    };

    // ##1 ++
    var opts = $.extend(true, {}, defaults, options);

    //apply events and styles for file input element
    var fileInput = $(this)
        .addClass('customfile-input') //add class for CSS
        .mouseover(function(){ upload.addClass('customfile-hover'); })
        .mouseout(function(){ upload.removeClass('customfile-hover'); })
        .focus(function(){
            upload.addClass('customfile-focus'); 
            fileInput.data('val', fileInput.val());
        })
        .blur(function(){ 
            upload.removeClass('customfile-focus');
            $(this).trigger('checkChange');
         })
         .bind('disable',function(){
             fileInput.attr('disabled',true);
            upload.addClass('customfile-disabled');
        })
        .bind('enable',function(){
            fileInput.removeAttr('disabled');
            upload.removeClass('customfile-disabled');
        })
        .bind('checkChange', function(){
            if(fileInput.val() && fileInput.val() != fileInput.data('val')){
                fileInput.trigger('change');
            }
        })
        .bind('change', function() {
            // ##5 ++
            if (opts.showInputText) {

                //get file name
                var fileName = $(this).val().split(/\\/).pop();

                $(this).data('text', fileName);

                //get file extension
                var fileExt = 'customfile-ext-' + fileName.split('.').pop().toLowerCase();

                //change text of button
                // uploadButton.text('Change'); // ##2 --
                uploadButton.text(opts.changeText); // ##2 ++

                //update the feedback
                uploadFeedback
                    .text(fileName) //set feedback text to filename
                    .removeClass(uploadFeedback.data('fileExt') || '') //remove any existing file extension class
                    .addClass(fileExt) //add file extension class
                    .data('fileExt', fileExt) //store file extension for class removal on next change
                    .addClass('customfile-feedback-populated'); //add class to show populated state


                autoTruncateFileName();
            }

            if ($.isFunction(opts.onChange)) {
                opts.onChange.apply(this, arguments);
            }
        })
        .click(function(){ //for IE and Opera, make sure change fires after choosing a file, using an async callback
            fileInput.data('val', fileInput.val());
            setTimeout(function(){
                fileInput.trigger('checkChange');
            },100);
        });

    //create custom control container
    var upload = $('<div class="customfile"></div>');

    // ##1 ++
    upload.css({
        width: opts.width
    });

    //create custom control button
    // ##2
    var uploadButton = $('<span class="customfile-button" aria-hidden="true"></span>').html(opts.buttonText).appendTo(upload);
    //create custom control feedback
    // ##2
    var uploadFeedback = $('<span class="customfile-feedback" aria-hidden="true"></span>').html(opts.inputText).appendTo(upload);

    // ##3
    if (opts.maxFileSize > 0 && $('input[type="hidden"][name="MAX_FILE_SIZE"]').length == 0) {
        $('<input type="hidden" name="MAX_FILE_SIZE">').val(opts.maxFileSize).appendTo(upload);
    }


    // ##4 ++
    var autoTruncateFileName = function () {
        //get file name
        var fileName = fileInput.val() || opts.inputText;

        if (fileName.length) {
            var limit = 0, // ensuring we're not going into an infinite loop
                trimmedFileName = fileName;
            uploadFeedback
                .text(fileName)
                .css({ display: 'inline' });
            while (limit < 1024 && trimmedFileName.length > 0 && uploadButton.outerWidth() + uploadFeedback.outerWidth() + 5 >= uploadButton.parent().innerWidth()) {
                trimmedFileName = trimmedFileName.substr(0, trimmedFileName.length - 1);
                uploadFeedback.text(trimmedFileName + '...');
                limit++;
            }
            uploadFeedback.css({ display: 'block' }); // ##4
        }
    };

    //match disabled state
    if(fileInput.is('[disabled]')){
        fileInput.trigger('disable');
    }

    uploadFeedback.data('text', opts.inputText);

    // ##5 ++
    if (! opts.showInputText ) {
        uploadFeedback.hide();
        uploadButton
            .css({
                float: 'inherit',
                display: 'block' // take up the full width of the parent container
            })
            .parent()
            .css({
                padding: 0
            });
    } else {
        uploadFeedback.css({
            display: 'block'
        });

        $(window).bind('resize', autoTruncateFileName);

    }


    //on mousemove, keep file input under the cursor to steal click
    upload
        .mousemove(function(e){
            fileInput.css({
                'left': e.pageX - upload.offset().left - fileInput.outerWidth() + 20, //position right side 20px right of cursor X)
                'top': e.pageY - upload.offset().top - $(window).scrollTop() - 3
            });    
        })
        .insertAfter(fileInput); //insert after the input
    
    fileInput.appendTo(upload);
        
    //return jQuery
    return $(this);
};








/**
 * ----------------------------------------------------------
 * EXAMPLE
 */
$(function(){

    // default
        $('#file_A').customFileInput();

    // percentage width
        $('#file_B').customFileInput({
            width: '50%'
        });

    // Custom Text
        $('#file_C').customFileInput({
            width: '30%',
            buttonText: 'Select File',
            changeText: 'Change File',
            inputText: 'Please select a file'
        });

    // Very short field (to test auto-truncated file name)
        $('#file_D').customFileInput({
            width: 150,
            inputText: ''
        });

    // button only
        $('#file_E').customFileInput({
            width: 90,
            buttonText: 'Upload File',
            changeText: 'Change',
            inputText: '',
            showInputText: false
        });

    // Event Handlers
        $('#file_F').customFileInput({
            width: 250,
            onChange: function () {
                console.info(arguments);
            }
        });

});











Run Pen

External CSS

  1. //ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
  2. //ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js