# Multiple custom file input fields with jQuery

I've modified this example to better suit my needs in handling multiple custom file uploads on the same page. It also renders the filename or truncates if it is too long for each of the 3 file fields. This solution works well however, I think it could be DRY'd out a lot? This is where I landed:

$(document).on('change', '.compliance-guide :file', function() { var input =$(this),
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [label]);
});

$(document).on('change', '.compliance-other :file', function() { var input =$(this),
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [label]);
});

$(document).on('change', '.agreement-file :file', function() { var input =$(this),
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [label]);
});

$(document).ready( function() {$('.compliance-guide :file').on('fileselect', function(event, label) {
var log = label;
if( log.length > 14 ) {
$('.compliance-guide-name').html(log.substring(0,14)+'...'); } else {$('.compliance-guide-name').html(log);
}
});
$('.compliance-other :file').on('fileselect', function(event, label) { var log = label; if( log.length > 14 ) {$('.compliance-other-name').html(log.substring(0,14)+'...');
} else {
$('.compliance-other-name').html(log); } });$('.agreement-file :file').on('fileselect', function(event, label) {
var log = label;
if( log.length > 14 ) {
$('.agreement-file-name').html(log.substring(0,14)+'...'); } else {$('.agreement-file-name').html(log);
}
});
});


You could certainly combine the first 3 handlers into 1, since they do the exact same thing:

$(document).on('change', ':file', function () { var input =$(this),
filename = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [filename]);
});


That'll add an event handler to all file inputs on the page.

You can simplify the replace stuff a bit; there's no need to make backslashes into regular slashes, if you're just going to throw it all away afterward. So it'd be simpler to say

input.val().replace(/^.*[\\\/]/g, '')


i.e. remove everything up-to-and-including the last slash.

Handling the custom fileselect event is where you want to limit it to specific inputs. However, you can extract the truncate logic, to clean things up, and avoid the repetition.

function truncateFilename (filename) {
if(filename.length > 14) {
return filename.substring(0, 14) + "...";
}
return filename;
}


And since you've stuck to a naming convention for the elements you want to display the filenames, you can create a function to set up your event handling:

function addFileSelectHandler(inputClass) {
$(inputClass).on("fileselect", function (event, filename) {$(inputClass + "-name").text(truncateFilename(filename));
});
}



Other notes:

• Don't use .html() when you're just setting plain text strings; use .text() (as seen above)
• Don't use classes to identify unique elements; use IDs. It's what they're for.
• CSS has a text-overflow rule that'll auto-truncate text if it overflows - no need for JavaScript at all, and no need for a hard-coded length of 14. As always, letter-count has little to do with how much space it takes to display; "m"s take up a lot more space than "i"s (if you're using a non-monospaced font).

Also, you could choose to handle all of this in the generic $(:file).on("change",.. function. Instead of using specific classes on the elements that should show a filename, you could use a data-input attribute to link them to a file input. For instance, you could have this markup: <input type="file" id="some-file"> <span class="filename" data-input="some-file"></span> <input type="file" id="some-other-file"> <span class="filename" data-input="some-other-file"></span>  And this JS: $(document).on('change', ':file', function () {
var input = $(this), filename = input.val().replace(/^.*[\\\/]/g, '');$(".filename[data-input='" + this.id + "']").text(filename);

// you can still trigger your custom event, if you want
// input.trigger('fileselect', [filename]);
});


That'll automatically show the filename, provided there's an element to show it. If there's no element, well, then it's not shown.

• What a wonderful answer. Insightful, informative, articulate and extremely helpful - thank you so much for your time! Bit of a rookie error re the use of id's I shouldn't have missed that. Are there any cross browser issues regarding your feedback I should be aware of? – Thomas Taylor Mar 4 '15 at 1:28
• @ThomasTaylor Thanks. And no, I can't think of any browser issues here. If it runs jQuery to begin with, it should run this just fine. But test it thoroughly regardless; I've been wrong before. – Flambino Mar 4 '15 at 1:37
• Great just doing some testing now - seems to be ok. Thanks again. – Thomas Taylor Mar 4 '15 at 4:46