You must be knowing that file inputs are very limited in terms of customization and many complex plugins are avaialble with multiple evacuations that allows you to customize those file inputs, but its is still a challenging task to get it worked.

This tutorial will help you in developing jQuery plugin which will assist you to replace that complex file input with support for multiple files.

Create a Custom File Input with PHP, CSS3 and jQuery With few Simple Steps Given Below

Setting up the Project

First create a folder say customFile alomg with 3 files jquery.customFile.js, jquery.customFile.css and customFile.html. To start swiftly, let this html template and copy paste the html code given below in your project.

We have now html template code and html template, now you need to open html file and then add a file input and a container along with its label:

<div class=”customfile-container”>
<label>File: </label>
<input type=”file” id=”file” name=”myfiles[]” multiple />
</div>

You need not to miss to giving it an id along with an array name like myfiles[] which will enable server to retrieve all the file names with the IE evacuation which we are going to cover later in this tutorial.

Now open query.customFile.js and then setup a jQuery plugin boilerplate:

;(function( $ ) {
$.fn.customFile = function() {
return this.each(function() {
var $file = $(this).addClass(‘customfile’); // the original file input
// code here
});
};
}( jQuery ));

Finally call the plugin in your markup.

How it Works

To develop the replacement, a simple markup structure will be required.

Click the “open” button to trigger a “click” event on the original file input. After selecting a file, the original input will trigger a “change” event, where the value of the input will be entered

Clicking the “open” button will trigger a “click” event on the original file input. After choosing a file the original input triggers a “change” event, where we’ll set the value of the input by accessing the file array if the file API is supported, or by accessing the original value otherwise.

Building the Plugin

Firstly, test the web browser for multiple support. The simplest method to build an input and check if the plugin is having a support of multiple property otherwise the web browser does not support multiple files. For later fixes, we also need to whether the browser id IE or not. This code can be taken outside the plugin because the plugin is not dependent on the element itself.

// Browser supports HTML5 multiple file?
var multipleSupport = typeof $(‘<input/>’)[0].multiple !== ‘undefined’,
isIE = /msie/i.test( navigator.userAgent ); // simple but not super secure..

Now let us build the elements required for the substitute. IE has very strict security measures to stop the filename from getting retreived if the input will be triggered outwardly, so inorder to avoid this situation, you need to make use of label not a button. By triggering the event on the label instead through a button, the issue would be resolved.

var $wrap = $(‘<div class=”customfile-wrap”>’),
$input = $(‘<input type=”text” class=”customfile-filename” />’),
$button = $(‘<button type=”button” class=”customfile-upload”>Open</button>’);
$label = $(‘<label class=”customfile-upload” for=”‘+ $file[0].id +'”>Open</label>’)

The type=”button” attribute is required for consistency, which would prevent some web browsers from submitting the form.

Next, let’s “get rid” of the original input. Instead of hiding it, let’s remove from the viewport by shifting it to the left, that way we can still use it even if it’s not visible; this is useful to trigger events that may be problematic if the input is literally hidden.

Next,get rid of the original input. No need of hiding it but you need to shift it to the left to remove it from the viewport which will allow it to use even if it is not visible. This will be beneficial for triggering the events which might create problems if the input is completely hidden.

$file.css({
position: ‘absolute’,
left: ‘-9999px’
});

Now let us fix the new elements to the DOM finally.

$wrap.insertAfter( $file ).append( $file, $input, ( isIE ? $label : $button ) );

At this point of time, you should have something that should look like the screenshot below, IE will be taken care of later.

Attaching the Events

The very thing you are required to carry out is to stop the original input from acquiring focus along with the newly developed button . Only the text input should be allowed to gain the focus.

$file.attr(‘tabIndex’, -1);
$button.attr(‘tabIndex’, -1);

Now, to open the dialog box,you need to trigger the click event on the button. Without any extra effort, dialog would be triggered by the label as there is no real button available in Internet Explorer

$button.click(function () {
$file.focus().click(); // Open dialog
});

The focus event is required to get triggered on some web browsers in order to get the click event work efficiently. If you would attempt clicking “open” in your web browser, the file dialog should be opened at this point of time.

Now, the triggered change event can be used after selecting a file to enter the value of the text input with the selected files.

Now we can use the change event that is triggered after choosing a file to fill the value of the text input with the chosen file(s).

$file.change(function() {
var files = [], fileArr, filename;
// If multiple is supported then extract
// all filenames from the file array
if ( multipleSupport ) {
fileArr = $file[0].files;
for ( var i = 0, len = fileArr.length; i < len; i++ ) {
files.push( fileArr[i].name );
}
filename = files.join(‘, ‘);
// If not supported then take the value
// and remove the path to show only the filename
} else {
filename = $file.val().split(‘\\’).pop();
}
$input.val( filename ) // Set the value
.attr(‘title’, filename) // Show filename in title tootlip
.focus(); // Regain focus
});

Fallback for Old Browsers

The simplest fallback can be created with multiple inputs to permit multiple files.We create a fresh input when a file is selected and when the input is deleted,we delete the input as well.

The code given below will go after the plugin as it has been written for applying to all custom file input.

if ( !multipleSupport ) {
$( document ).on(‘change’, ‘input.customfile’, function() {

var $this = $(this),
// Create a unique ID so we
// can attach the label to the input
uniqId = ‘customfile_’+ (new Date()).getTime();
$wrap = $this.parent(),

// Filter empty input
$empty = $wrap.siblings().find(‘.customfile-filename’)
.filter(function(){ return !this.value }),

$file = $(‘<input type=”file” id=”‘+ uniqId +'” name=”‘+ $this.attr(‘name’) +'”/>’);

// 1ms timeout so it runs after all other events
// that modify the value have triggered
setTimeout(function() {
// Add a new input
if ( $this.val() ) {
// Check for empty field to prevent
// creating new inputs when changing files
if ( !$empty.length ) {
$wrap.after( $file );
$file.customFile();
}
// Remove and reorganize inputs
} else {
$empty.parent().remove();
// Move the input so it’s always last on the list
$wrap.appendTo( $wrap.parent() );
$wrap.find(‘input’).focus();
}
}, );
});
}

Customizing the Appearance

Each and everything must be working efficiently at this point of time, so let us add some styles to spice it up more.

/* It’s easier to calculate widths
* with border-box layout */
.customfile-container * {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
font: normal 14px Arial, sans-serif; /* Global font to use ems with precision */
}

.customfile-container {
width: 300px;
background: #FFF2B8;
padding: 1em;
}

.customfile-container label:first-child {
width: 100px;
display: block;
margin-bottom: .5em;
font: bold 18px Arial, sans-serif;
color: #333;
}

.customfile-wrap {
position: relative;
padding: 0;
margin-bottom: .5em;
}

.customfile-filename,
.customfile-upload {
margin: 0;
padding: 0;
}

.customfile-filename {
width: 230px;
padding: .4em .5em;
border: 1px solid #A8A49D;
border-radius: 2px 0 0 2px;
box-shadow: inset 0 1px 2px rgba(0,0,0,.2);
}
.customfile-filename:focus {
outline: none;
}

.customfile-upload {
display: inline-block;
width: 70px;
padding: .4em 1em;
border: 1px solid #A8A49D;
background: #ddd;
border-radius: 0 2px 2px 0;
margin-left: -1px; /* align with input */
cursor: pointer;
background: #fcfff4;
background: -moz-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
background: -webkit-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
background: -o-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
background: -ms-linear-gradient(top, #fcfff4 0%, #e9e9ce 100%);
background: linear-gradient(to bottom, #fcfff4 0%, #e9e9ce 100%);
}

.customfile-upload:hover {
background: #fafafa;
box-shadow: 0 0 2px rgba(0,0,0,.2);
}
.customfile-upload::-moz-focus-inner { /* Fix firefox padding */
padding: 0; border: 0;
}

You can customize the CSS according to your requirements.

Retrieving the Files on the Server

Now, add a submit button initially after wrapping up an input into a form.

form action=”test.php” method=”post” enctype=”multipart/form-data”>
<div class=”customfile-container”>
<label>File: </label>
<input type=”file” id=”file” name=”myfiles[]” multiple />
</div>
<button type=”submit”>Submit</button>
</form>

After that we would get all of the filenames and print them out in test.php file

$files = $_POST[‘myfiles’]; // Array containing all files
echo implode( $files, ‘
‘ );

As, the array name myfiles[] is being used, so the files will be retrieved by the server even if the fallback has been used.

Conclusion

Without putting any extra efforts, the file input is very simple to customize. However, the fallback is not a suitable solution but still it works as it is very easy to maintain with lines of codes and with many other technologies like silverlight, flash etc.

You can grab the full code here or play with the demo here.